I'm creating a small space invaders project and I have the aliens constantly in a left to right loop. When they hit the end of the screen they repaint on the right side going again left to right. I have set the window size already and I have looked at various tutorials on how to make space invaders in Java, however most of them say the same thing that I have tried. Is it possible someone can point out where I am going wrong with my coding so that I know how to fix it.
This is the code for the alien class. There are different aliens, however all the classes look pretty much the same as this one:
package spaceinvaders2;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.ImageIcon;
class Alien extends MoveObject
{
Image Alien = new ImageIcon(getClass().getResource("alien.gif")).getImage();
Alien(int x, int y, int w, int h)
{
super(x, y);
bounds.width = w;
bounds.height = h;
}
public void paint(Graphics g)
{
System.out.println("Alien generated");
bounds.x = bounds.x - 2;
if ((bounds.x + bounds.width)< 0)
{
bounds.x = 750;
dead = false;
}
g.drawImage(Alien,bounds.x,bounds.y,bounds.width, bounds.height, this);
}
}
Edit: My paint method is being called in the main game functionality part where it draws all the graphics. The drawing isn't the problem it is the movement of the alien which is in this class.
I think the paint Method ist just running once.
You have to add a thread to call the paint() method periodi.
You need to give the Alien a direction attribute (+1 or -1) which tells it which direction it is moving in. When it is about to go off-screen, flip the direction. For example, if the direction was +1 change it to -1 and vice versa.
Here is a simple example I created:
public class Alien extends JPanel
private int x = 5;
private int y = 5;
private int direction = 1;
#Override
public void paint(Graphics g) {
super.paint(g);
g.clearRect(x, y, getWidth(), getHeight());
// draw the alien.
g.drawRect(x, y, 10, 10);
// move it
x = x + 5 * direction;
// is it about to go off-screen?
if (x < 0 || x + 10 > getWidth()) {
// change the direction
direction *= -1;
}
}
}
According to your comment, you want the aliens to move from left to right once they hit the screen boundaries, instead of re-entering at the other side.
The solution is simple, you have to keep track of the direction of the alien. An easy way to do this is to make it's step size a variable.
You give your Alien class a step member like this:
int step = -2;
Then:
bounds.x = bounds.x + direction;
if ((bounds.x + bounds.width)< 0)
{
step = +2;
}
else if ((bounds.x - bound.width) > 750)
{
step = -2;
}
dead = false;
g.drawImage(Alien,bounds.x,bounds.y,bounds.width, bounds.height, this);
Off-topic, I think the dead = false doesn't belong in your paint method.
Related
I am trying to make a game and am almost done with the code. But I can't make my rectangle randomly fall down from my screen.
I am coding a car game that is supposed to dodge the other cars. But is not working. can someone help me?
int carx = 200;
int cary = 0;
float speedx = 0;
float speedy = 0;
float accy = 0.1;
color rod = color(255, 0, 0);
color vit = color(255);
final int START_STATE = 1;
final int PLAY_STATE = 2;
final int GAMEOVER_STATE = 3;
final int RESTART = 4;
int state = START_STATE;
void setup() {
size(400, 700);
}
void draw() {
switch (state) {
case START_STATE:
drawStart();
break;
case PLAY_STATE:
drawPlay();
break;
case GAMEOVER_STATE:
drawGameOver();
case RESTART:
drawrestart();
}
}
void init() {
carx = 200;
cary = 0;
speedx = 0;
speedy = 0;
accy = 0.1;
}
void drawStart() {
background(0);
textAlign(CENTER, CENTER);
textSize(16);
text("Click the mouse button to start the game", width / 2, height / 2);
if (mousePressed) {
state = PLAY_STATE;
}
}
void drawGameOver() {
textAlign(CENTER, CENTER);
textSize(20);
text("you have crashed your car", width / 2, height / 2);
if (mousePressed) {
state = PLAY_STATE;
init();
}
}
void drawrestart() {
textAlign(CENTER, CENTER);
textSize(15);
text("press mouse to restart", 200, 400);
if (keyPressed) {
state = RESTART;
}
}
void drawPlay() {
background(0);
if (get(carx, cary) == vit) {
speedy = -1 * speedy;
}
fill(rod);
rect(carx, cary, 50, 30);
if (get(mouseX, 600) == color(255, 0, 0)) {
state = GAMEOVER_STATE;
}
fill(#FFFFFF);
rect(mouseX, 600, 30, 50);
carx += speedx;
cary += speedy;
speedy += accy;
}
The code you have at the moment only has one rectangle fall down from the top for each 'round' of the game. I'm not sure if you wanted to have multiple blocks falling; I think that would be a good next step.
For now, here's a simple hack which will cause the block to fall from a random position each time, like you requested.
At the very start of your code, outside of the functions, place:
boolean randomise;
Then, within void init() you should add:
randomise = true;
Finally, add this section into drawPlay(), right at the start of the function:
if (randomise){
carx = int(random(width-50));
randomise = false;
}
Note that a new random x co-ordinate will only generate every time you set the boolean 'randomise' to true again. So if you generate a new iteration with more than one block falling inside the drawPlay() function, you should bear this in mind and adjust the code accordingly.
------- [EDIT] -------
Hi, glad that this helped.
I've actually noticed another little issue which I will help you fix.
Currently, you are checking at mouseX to see whether there has been a collision. This works (mostly), but if the right side of the player's white car drives through the left edge of a red falling block, then the game continues as though nothing has happened. What should occur is that the game is over because a collision is detected. You want to find out if any part of the two shapes have overlapped.
To do this, you should alter the code like so. In drawPlay(), replace:
if (get(mouseX, 600) == color(255, 0, 0)) {
state = GAMEOVER_STATE;
}
with:
if (get(mouseX, 600) == color(255, 0, 0) || get(mouseX + 30, 600) == color(255, 0, 0)) {
state = GAMEOVER_STATE;
}
This is an OR statement - checking whether either side of the player's car has collided. This way, every time they bump into each other, the game will end.
Now for your question: how to add multiple cars?
There are a few ways you could go about this, but I'll show you the most straightforward.
In drawPlay(), you want to add this little if statement:
if (cary > height){
init();
}
Basically what we're doing here is checking if the previous red block/car has fallen off the bottom of the sketch. If it has, i.e. if the red block's y co-ordinate is larger than the height of the whole sketch, we call init() again. This will reset everything, including making randomise true. Once randomise is true, the previous code you added will select a random start point for the block, and it will fall all over again.
I hope you've understood everything I explained - whilst my code will fix your problem, the best way to learn is to try to solve things by yourself. Check out Dan Shiffman's videos on YouTube. These are a great starting place to get to grips with Processing - hopefully you'll be more confident writing your own code after following along with his examples :)
Use random to generate a random x coordinate:
carx = (int)random(0, width-50);
Reset the position and the speed of the care, once the car reached to bottom of the window:
void drawPlay() {
if (cary > height) {
carx = (int)random(0, width-50);
cary = 0;
speedy = 0;
}
// [...]
I am currently working on a 3 cushion billiards game project. I have added two balls on the table so far. I am trying to move one of the balls but I am having a hard time doing that. Should I use a timer? If so then could you tell me an effective way to use the timer on my code so I can move my balls?
Your help would be much appreciated.
Thanks in advance.
Farhan Hasan
I have tried to create a move function for the class balls. But I am not sure what I should put inside the function, I have added the xSpeed and ySpeed. The xLocation and the yLocation changes depending on the xSpeed and ySpeed.
public class Balls
{
private Color ballFillColor;
private Color ballBorderColor;
private int ballX = 0;
private int ballY = 0;
private int xSpeed = 5;
private int ySpeed = 0;
private int ballWidth = 0;
private int ballHeight = 0;
Timer t;
public boolean fillBall = false;
private static Balls ballArray[]; //Required for drawMultipleBalls
Balls(){ //Constructor
ballBorderColor = Color.black;
}
Balls(int ballX, int ballY, int ballWidth, int ballHeight, Color ballBorderColor, JFrame window){ //Constructor
// X , Y , Width, Height, Border Colour, container
this.setBallBorderColor(ballBorderColor);
this.setBallWidth(ballWidth);
this.setBallHeight(ballHeight);
this.setBallX(ballX);
this.setBallY(ballY);
this.drawBall(window);
}
//Here is the move function. I am not really sure what to do here.
public void move()
{
if(this.ballX < 1000 - this.ballWidth)
{
this.ballX += this.xSpeed;
}
try
{
Thread.sleep(1);
}
catch(Exception e)
{
}
}
//GET AND SET FUNCTIONS HERE
//HERE ARE THE FUNCTIONS WHICH ARE RESPONSIBLE FOR DRAWING MY BALLS IN JFRAME
public void drawBall(JFrame frame)
{
frame.getContentPane().add(new MyComponent());
}
public void drawMultipleBalls(JFrame frame, Balls[] balls)
{
ballArray = balls;
frame.getContentPane().add(new MyComponent2());
}
private class MyComponent extends JComponent{
public void paintComponent(Graphics g){
if (fillBall) //Fill first, and then draw outline.
{
g.setColor(ballFillColor);
g.fillOval(getBallX(),getBallY(), getBallHeight(),getBallWidth());
}
g.setColor(getBallBorderColor());
g.drawOval(getBallX(),getBallY(), getBallHeight(),getBallWidth());
}
}
private class MyComponent2 extends JComponent{
public void paintComponent(Graphics g){
for (int i = 0; i < ballArray.length; i++)
{
if (ballArray[i].fillBall) //Fill first, and then draw outline.
{
g.setColor(ballArray[i].ballFillColor);
g.fillOval(ballArray[i].getBallX(),ballArray[i].getBallY(), ballArray[i].getBallHeight(),ballArray[i].getBallWidth());
}
g.setColor(ballArray[i].getBallBorderColor());
g.drawOval(ballArray[i].getBallX(),ballArray[i].getBallY(), ballArray[i].getBallHeight(),ballArray[i].getBallWidth());
}
}
}
Hopefully, I can have two movable balls for the game, the should bounce back as the hit the edge of the screen and they should be able to slow down over time. For that, I am thinking to use a damper (I will multiply the xSpeed and ySpeed with a number less than 1, eventually it will slow down the ball)
Here is a simple example I came up with to show a ball moving and bouncing off the edges.
The direction changes based on the boundary. Left and top edges just check for 0. Bottom and right edges need to include the diameter of the ball.
The x and y increments are independent. And these amounts in conjunction with the timer can change the movement. Notice however, that to have objects bounce off of each other (as in a pool game) is more complicated due to angle of trajectories, etc. And the distances bounced will vary and slow with time based on frictional values. Everything else is documented in the Java API.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class MovementDemo extends JPanel implements ActionListener {
JFrame frame = new JFrame("Movement Demo");
int size = 500;
int x = 50;
int y = 200;
int diameter = 50;
int yinc = 2;
int xinc = 2;
int xdirection = 1;
int ydirection = 1;
public MovementDemo() {
setPreferredSize(new Dimension(size, size));
frame.add(this);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new MovementDemo().start());
}
public void start() {
Timer timer = new Timer(100, this);
timer.setDelay(5);
timer.start();
}
public void actionPerformed(ActionEvent ae) {
if (x < 0) {
xdirection = 1;
}
else if (x > size - diameter) {
xdirection = -1;
}
if (y < 0) {
ydirection = 1;
}
else if (y > size - diameter) {
ydirection = -1;
}
x = x + xdirection * xinc;
y = y + ydirection * yinc;
repaint();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.BLUE);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.fillOval(x, y, diameter, diameter);
}
}
It seems in general there are a few things you need to figure out:
has the ball collided with another ball
has the ball collided with a wall
otherwise just figure out what is the ball's new position based on its velocity
Below is some sample code that stubs some of this out. You can first compare the current ball's position to all others (not including the current ball of course). If there are any equal positions, process a collision with a ball. If the ball is at the window border i.e it hit a wall, process a collision with a wall. Otherwise just calculate its new position based on its current velocity.
The process collision part is just to apply physics mechanics to whatever degree of complexity you require. One general suggested change would be to update the velocity of the balls then apply it to the position after. The specific calculations for velocity changes you could apply as needed and as you can imagine it can get pretty involved which is why I suggest using a separate method and possibly a sub class for velocity instead of managing each part of the velocity vector in the ball itself. I used the wall as an object because of this. The composition, weights, velocities etc of the object's colliding can affect the resulting collision, but how complex you want that processing to be is up to you.
Sorry I'm no physics expert but I hope this sends you in the right direction in terms of code! Also this might help with the specific calculations you might want to use:
https://www.khanacademy.org/science/physics/one-dimensional-motion/displacement-velocity-time/v/calculating-average-velocity-or-speed
public void move()
{
// check if balls are on same position not including this ball
for(Ball b: ballArray){
if (this.position == b.position && this != b){
processCollision(this, b, null);
} else{
// if the ball hasn't collided with anything process its movement based on speed
// this assumes a 1000 x 1000 window for keeping objects inside it
if(this.ballX < 1000 - this.ballWidth && this.ballY < 1000 - this.ballHeight){
this.ballX += this.xSpeed;
this.ballY += this.ySpeed;
}else {
processCollision(this, null, new Wall());
}
}
}
try
{
Thread.sleep(1);
}
catch(Exception e)
{
}
}
public void processCollision(Ball b1, Ball b2, Wall w){
// if ball hasn't collided with a wall, process a ball - ball collision
if(w == null){
// apply physics mechanics according the complexity desired for ball collisions
b1.xSpeed -= b2.xSpeed;
b1.ySpeed -= b2.ySpeed;
// ball 2 would end up slowing down
b2.xSpeed -= b1.xSpeed;
b2.ySpeed -= b1.ySpeed;
}
// if ball hasn't collided with a ball, process a ball - wall collision
if(b2 == null){
// apply physics mechanics for hitting a wall
// e.g as below: just send ball in opposite direction
b1.xSpeed = b1.xSpeed * -1;
b1.ySpeed = b1.ySpeed * -1;
}
// either way, process ball's new position based on its new speed
b1.ballX += b1.xSpeed;
b1.ballY += b1.ySpeed;
b2.ballX += b2.xSpeed;
b2.ballY += b2.ySpeed;
}
I am trying to change the y position of the rectangle, however, whenever I try to, it expands/gets bigger vertically.
public class PlayerPaddle implements Paddle {
double yVelocity;
final double GRAVITY = 0.94;
//move up/down faster (if not accelerating = slow down)
boolean upAccel, downAccel;
//determines if player 1 or player 2 (on left or on right)
int player;
//position of actual paddle
int x;
double y;
Rectangle panel;
public PlayerPaddle(int player) {
upAccel = false;
downAccel = false;
y = 210; //not moving initially
yVelocity = 0;
if (player == 1) {
//left side
x = 20;
} else {
//right side
x = 660;
}
}
#Override
public void draw(Graphics g) {
//draw paddle
g.setColor(Color.WHITE);
g.fillRect(x, (int) y, 20, 80);
}
#Override
public void move() {
if (upAccel) {
yVelocity -= 2;
} else if (downAccel) {
yVelocity += 2;
} else if ((!upAccel) && (!downAccel)) {
yVelocity *= GRAVITY;
}
y += yVelocity; //changes y position of paddle
}
public void setUpAccel(boolean input) {
upAccel = input;
}
public void setDownAccel(boolean input) {
downAccel = input;
}
#Override
public int getY() {
return (int) y;
}
}
I want to know how to make the rectangle move up and down vertically. A similar question had only one answer which said that the previously painted rectangle was not being cleared and as a result is expanding. But even when I say g.clearRect(...) it still expands and does not move.
I am new to Swing and Awt but I am really committed to learning. Thanks for the help.
A similar question had only one answer which said that the previously painted rectangle was not being cleared and as a result is expanding
And that is still probably the problem here.
Somewhere, not in the code presented here, you need to invoke the draw(...) method of this class.
So in that code you need to make sure the background of the component is cleared before you draw the paddle. Since you should be overriding the paintComponent(...) method of a panel to do custom painting your code should look something like:
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
// draw the paddle
}
Read the section from the Swing tutorial on Custom Painting for more information and working examples.
I am new to Swing and Awt but I am really committed to learning.
Then keep a link to the tutorial handy for all Swing basics.
I'm making a game where you're controlling a square, and objects will spawn in random places, which you have to pick up to get points, while also having to dodge big squares going from side to side. Right now I have 2 classes (one for enemies - big squares -, and one for the hero), I haven't done the point system yet with the spawning objects, but that's not what I'm trying to do now.
So my problem right now is that I don't really know how to make the person lose the game/a life when you touch the "enemies" with your hero. I would know how to do it without classes, but I'd like to know how to do it when they are in separate classes.
If someone could explain with code and comments how this would be done, it would help me out a lot :) (I read something about 'extends' for classes but I'm not sure if this is what I should use or not).
Here's a screenshot of what my game looks like at the moment, just to better illustrate it:
Here's the main code page:
Hero myHero = new Hero(400,480,5);
Enemies myEnemies = new Enemies(50,50,10);
Enemies myEnemies2 = new Enemies(50,350,15);
Enemies myEnemies3 = new Enemies(50,650,12);
void setup() {
size(900,800);
frameRate(30);
smooth();
}
void draw() {
background(0);
myHero.keyPressed();
myEnemies.enemyDisplay();
myEnemies.enemyMove();
myEnemies2.enemyDisplay();
myEnemies2.enemyMove();
myEnemies3.enemyDisplay();
myEnemies3.enemyMove();
}
Class 1:
class Enemies {
float xpos, ypos, speed;
Enemies(float x, float y, float s) {
xpos = x;
ypos = y;
speed = s;
}
void enemyDisplay() {
rect(xpos, ypos, 100, 100);
}
void enemyMove() {
xpos += speed;
if((xpos > width - 100) || (xpos < 0)) {
speed *= -1;
}
}
}
Class 2:
class Hero {
float xpos_, ypos_, speed_;
Hero(float x, float y, float s) {
xpos_ = x;
ypos_ = y;
speed_ = s;
}
void keyPressed() {
if (key == CODED) {
if (keyCode == UP) {
ypos_ -= speed_;
}
if (keyCode == DOWN) {
ypos_ += speed_;
}
if (keyCode == LEFT) {
xpos_ -= speed_;
}
if (keyCode == RIGHT) {
xpos_ += speed_;
}
}
rect(xpos_,ypos_,30,30);
}
}
I believe the question you are asking involves basic collision detection and object interactions.
I would first make the Enemies a List and create it / add elements during the setup() call:
List<Enemies> enemies = new List<Enemies>();
enemies.add(new Enemies(50,50,10));
This allows you to store all of your Enemies under one object. So your draw() method would look something like:
void draw(){
background(0);
myHero.keyPressed();
for(Enemies enemy : enemies)
{
enemy.enemyDisplay();
enemy.enemyMove();
if (hero.isCollidingWith(enemy)) // collision method defined in the hero object, but you could define it in the Enemies class as well, it doesn't really matter
{
hero.removeHealth(); // method defined in hero that removes health
}
}
}
This method would be in one of your classes:
public boolean isColliding(Enemies enemy)
{
// check the x and y coordinates of each object
}
I hope this helps to point you in the right direction.
you need to figure out collision detection and when your objects collide with each other, something basic like:
class Enemy
{
//...
public boolean isColliding(Hero hero)
{
//figure out the distance between two objects, if its less than their size, they are colliding..
//...
}
//...
}
then you need a part of your Game Loop that checks if anything is colliding with your hero, pickups, walls, etc...
It seems the first part you need help with is collision detection. The short answer I would give that will almost undoubtedly lead you to more questions is to look at the Area class (specifically Area.intersect). You might also want to look at the classes I've put together for displaying & managing areas in this project here
There are several problems here that touch on both application design as well as conventions. These should be addressed first before trying to tackle the collision detection problem.
The Enemies class only represents a single enemy, so the name of the class should reflect that. Additionally, prefixing the method names with "enemy" is redundant and can be removed. Other changes have been commented in the revised class below.
public class Enemy {
// Instead of hard-coding in the width and height of an enemy, allow the
// instantiating code to specify the enemy's size. This will allow you
// to have different size enemies and prevents you from having "magic numbers"
// in your code.
private float xpos, ypos, width, height, speed;
public Enemy(float x, float y, float s, float w, float h) {
xpos = x;
ypos = y;
width = w;
height = h;
speed = s;
}
/* These getters will be used for collision detection later */
public float getX() {
return xpos;
}
public float getY() {
return ypos;
}
public float getWidth() {
return width;
}
public float getHeight() {
return height;
}
// I've changed `display` to `draw` to be consistent with the method name in
// your main `draw` method.
public void draw() {
rect(xpos, ypos, width, height);
}
// This method now accepts a screenWidth parameter so that the enemy can know
// when they've collided with the left or right wall of the screen without
// having to rely on an global variable.
public void move(int screenWidth) {
xpos += speed;
if ((xpos > screenWidth - width) || (xpos < 0)) {
speed *= -1;
}
}
}
I mention the "magic numbers" in one of the comments above. See this wikipedia article for more on that.
The Hero class contains property names that have an underscore postfix. This ranges from unconventional to inconsistent with respect to all of your other property names in your other classes. The original keyPressed() method mixes the logic for both drawing and moving. These two things have been separated and the methods named like those of the Enemy class for consistency.
public class Hero {
private float xpos, ypos, width, height, speed;
public Hero(float x, float y, float s, float w, float h) {
xpos = x;
ypos = y;
width = w;
height = h;
speed = s;
}
// Change this method name to draw for consistency with the Enemy class
public void draw() {
// Key press functionality has been moved to the `move` method for consistency
// with the Enemy class.
rect(xpos, ypos, WIDTH, HEIGHT);
}
// This method uses the variables key, keyCoded, UP, DOWN, LEFT, and RIGHT. You
// did not include any import statements with your code, so they may be coming
// from there; however, if they are globals, you should pass them to this method
// as arguments whenever you call it.
public void move() {
// If this condition isn't satisfied, return immediately. This prevents
// unnecessary nesting and work below.
if (key != CODED) {
return;
}
if (keyCode == UP) {
ypos -= speed;
}
// Use `else if` here and below to prevent multiple unnecessary
// comparisons of keyCode.
else if (keyCode == DOWN) {
ypos += speed;
}
else if (keyCode == LEFT) {
xpos -= speed;
}
else if (keyCode == RIGHT) {
xpos += speed;
}
}
public boolean isColliding(Enemy enemy) {
// Collision detection is easy since all of your game entities (the hero and
// the enemies) are all rectangles and axis-aligned (not rotated). You can
// use a method known as "bounding box intersection."
return (Math.abs(enemy.getX() - xpos) * 2 < (enemy.getWidth() + width))
&& (Math.abs(enemy.getY() - ypos) * 2 < (enemy.getHeight() + height));
}
}
For more on bounding box intersection, see this gamedev stackexchange question.
Now that your classes are in order, its time to address your main code. We'll need to update the method names and, as #James T suggested, you should make a list of enemies instead of creating a new independent object for each enemy. This will make it easier for you to add or remove enemies in the future and to be able to process all enemies with one block of code without repeating yourself.
// Use constants to remove magic numbers.
private static final int SCREEN_WIDTH = 900;
private static final int SCREEN_HEIGHT = 800;
private Hero myHero = new Hero(400, 480, 30, 30, 5);
private List<Enemy> enemies = new ArrayList<Enemy>();
void setup() {
size(SCREEN_WIDTH, SCREEN_HEIGHT);
frameRate(30);
smooth();
enemies.add(new Enemy(50, 50, 100, 100, 10));
enemies.add(new Enemy(50, 350, 100, 100, 15));
enemies.add(new Enemy(50, 650, 100, 100, 12));
}
void draw() {
hasCollision = false;
background(0);
// I've changed the order of draw->move to move->draw. If you draw first, then
// move, then detect collisions, it will appear to your user that your hero has
// not yet collided with an enemy even though you act as they have (e.g.: they
// will not see the collision until the next time you draw the scene).
myHero.move();
myHero.draw();
for (Enemy enemy : enemies) {
enemy.move();
enemy.draw(SCREEN_WIDTH);
if (!hasCollision && myHero.isColliding(enemy)) {
hasCollision = true;
}
}
if (hasCollision) {
// Handle enemy collision here
}
}
You'll notice that I've also added accessibility modifiers to everything. While it is technically valid to exclude them and use the defaults, it makes your code more readable to include them because it is more obvious. When you're first starting out, the more obvious the better.
Alright got it to work thanks to all you helpful wonderful people!
I did this:
public boolean isColliding(Enemies h){
float distance = dist(x,y,h.x,h.y);
if(distance<100){
return true;
}else{
return false;
}
}
and in my draw(){ I have
if(myHero.isColliding(myEnemies)){
println("You lost!");
}
I had a very similar 'fix' earlier, but the reason I got an error was because I had Hero h instead of Enemies h in the 'if' function, so it was just a very dumb error that I overlooked :P
I'm having an issue showing that one rectangle has collided with another. So my question is, how can I get the intersect method to check for collision? Or are there any other ways to handle collision in this situation?
I'm creating a turn-based combat game (similar to Final Fantasy or Legend of Dragoon) where the player's character is on the right side of the screen and the enemy is on the left side. The player and enemy each take turns attacking. So when the player attacks, the sprite animation moves across the screen from right to left until it stops in front of the enemy, attacks, and returns to it's starting coordinates. Both the player and enemy have a rectangle drawn around them to represent the bounds of each character.
When the player moves forward, he passes through the Enemy's rectangle and stops within it. At this point there should be output to the console saying "INTERSECT!" to show that there was a collision between the two rectangles, but unfortunately there isn't.
Please note that I have omitted the unnecessary pieces of code within my classes and tried to provide the code that pertains to my problem.
This is my entry point, GameClass:
public class GameClass extends BasicGame{
//other variable declarations
public void init(GameContainer container) throws SlickException {
player = new Player();
enemy = new Enemy();
skeleton = new Skeleton();
enemy = skeleton;
playX = player.getStartX(); //700
playY = player.getStartY(); //140
playW = player.rect.getWidth(); //40
playH = player.rect.getHeight(); //70
enemyX = enemy.getStartX(); //100
enemyY = enemy.getStartY(); //140
enemyWidth = enemy.getWidth(); //50
enemyHeight = enemy.getHeight(); //55
SpriteSheet sheet = new SpriteSheet("data/homeranim.png", 36, 65);
anim = new Animation();
for (int i=0;i<8;i++) {
anim.addFrame(sheet.getSprite(i,0), 150);
}
}
public void render(GameContainer container, Graphics g)
throws SlickException {
anim.draw(playX,playY); // draws player animation
skeletonAnim.draw(enemyX, enemyY); // draws enemy
g.draw(player.rect); //draws player bounds
g.draw(enemy.rect); //draws enemy bounds
}
public void update(GameContainer container, int delta)
throws SlickException {
playerUpdate(delta);
if (player.rect.intersects(enemy.rect)) {
System.out.println("INTERSECT!");
System.out.println("Player minX: " + player.rect.getMinX());
System.out.println("Player maxX: " + player.rect.getMaxX());
System.out.println("Enemy minX: " + enemy.rect.getMinX());
System.out.println("Enemy maxX: " + enemy.rect.getMaxX());
}
}
public void playerUpdate(int delta) {
if (playerForward == true){
playX -= delta * 0.4f;
if (playX <= 140) {
playX = 140;
playerForward = false;
playerBackward = true;}
}
if (playerBackward == true) {
playX += delta * 0.4f;
if (playX >= 700) {
playX = 700;
playerBackward = false;
delay = 1250;
}
public void keyReleased(int key, char c) {
if (key == Input.KEY_ENTER){
playerForward = true;}
}
}
This is a glimpse at my Player class:
public class Player {
private int startX = 700;
private int startY = 140;
public Shape rect = new Rectangle(startX, startY, 40, 70);
//plus getters and setters
}
And my entire Enemy class:
public class Enemy {
private int startX, startY, width, height;
public Shape rect = new Rectangle(startX, startY, width, height);
// plus getters and setters
}
My Skeleton class extends Enemy:
public class Skeleton extends Enemy {
public Skeleton() {
// TODO Auto-generated constructor stub
setMaxHealth(120);
setStartX(100);
setStartY(140);
setWidth(50);
setHeight(55);
}
}
Note: Since I've switched g.drawRect() to g.draw(), enemy rectangle isn't being drawn.
Rect bounds at starting point: http://i.imgur.com/QDDk858.png
Rect bound where collision should be: http://i.imgur.com/pOANfvN.png
I hope I've provided enough code to show you what my problem is. I've rummaged through the internet for hours with no luck. If there is any other code I need to provide, please do not hesitate to ask. Thank you very much for your help and support!
You are not updating the hitbox positions themselves.
You are drawing this:
g.drawRect(playX, playY, playW, playH); //draws player bounds
g.drawRect(enemyX, enemyY, enemyW, enemyH); //draws enemy bounds
But this isn't the actual hitbox, it's just the position of the player/enemy and the rectangles drawn here will be on the correct position while the hitboxes themselves aren't.
I suggest you do the following:
public void update(GameContainer container, int delta)
{
playerUpdate(delta);
player.rect.setLocation(playX, playY);
enemy.rect.setLocation(enemyX, enemyY); // update the hitboxes to the new positions
if (player.rect.intersects(enemy.rect))
{
System.out.println("INTERSECT!");
}
}
public void playerUpdate(int delta)
{
if (playerForward == true)
{
playX -= delta * 0.4f;
if (playX <= 140)
{
playX = 140;
playerForward = false;
playerBackward = true;
}
}
if (playerBackward == true)
{
playX += delta * 0.4f;
if (playX >= 700)
{
playX = 700;
playerBackward = false;
delay = 1250;
}
}
}
public void keyReleased(int key, char c)
{
if (key == Input.KEY_ENTER)
{
playerForward = true;
}
}
Furthermore, as you seem to be new to game development in Java, some tips for you:
Format your code properly
Always place full {...} after if, else, switch, while, for, etc.; proper line indentation, .
Think OO (Object-Oriented)
This one is pretty important. Your enemy and player class should both extend some kind of entity class because they both will pretty much want to obtain similar behavior (avoid code duplication!). Sum up similar behavior to a super class, simplify the behavior to be controlled with a few adjustable parameters and so on.
For example, you store the positions of your enemy and player as a static integer in your main class. This is not OO. Move the positions to the entity class where you can implement it in whatever manner you wish.
Don't just throw an exception for no reason
Your update(...) method throws a SlickException even though it's never needed.
Be careful about encapsulation
This is something a lot of beginners do: Just grab some parameters, put them in a class as private (or maybe even public) and generate getters- and setters for them. This is not encapsulation. This is almost as bad as making them public in the first place.
But why don't we just make everything public?
We don't want anyone (or even ourselves) to rely on some parameters that just happen to be there because of some very specific implementation of something we might want to change later. Don't just put all possible values out there to be changed, the sense of encapsulation is to be independent from what kind of implementation we end up using and to protect the usability of our code by guarding what can be set/changed.
Performance does matter
This is one of the aspects you should watch out for, for any kind of software, but you can often most drastically see the consequences in games. Performance is important! And by that, I don't mean that you have to watch out for every single detail, but just keep an overview in mind on how to improve and fasten up your code, especially with frequently called methods such as update(..) and render(..) in Slick2D.
Update
As a solution to another problem discussed in the comments:
public class Enemy {
private int startX, startY, width, height;
public Shape rect = new Rectangle(startX, startY, width, height);
// plus getters and setters
}
width and height can only be 0 as they are never assigned an integers have the value 0 per default, so the enemy rectangle hitbox does have 0 width and will never trigger.
Try something like:
public class Enemy {
private int startX, startY, width = 50, height = 70;
public Shape rect = new Rectangle(startX, startY, width, height);
// plus getters and setters
}
This should work, but you should probably move all these attributes to the enemy class and put them in the constructor.