I have an arraylist of popcorn made by the following:
public class Popcorn
{
int posX;
int posY;
int speed;
int taps;
public Popcorn(int x, int y)
{
posX = x;
posY = y;
speed = PowerConstants.popcorn_speed;
taps = 0;
}
public void tick()
{
posY += speed;
}
public int getX()
{
return posX;
}
public int getY()
{
return posY;
}
public void addTap()
{
taps++;
}
public int getTaps()
{
return taps;
}
}
I have different powerups I want to use on these popcorn, one of which involves making the popcorn fall faster down the screen (dealing therefore with the speed) for a certain amount of time. The powerup works (and so does the timing system); however, it only affects newly created popcorn, not the popcorn that are already on the screen. Here is the relevant code:
ArrayList <Popcorn> popcorns;
Image happyPopcorn;
popcorns = new ArrayList<Popcorn>();
happyPopcorn = Assets.happyPopcorn;
//This section is in the updateRunning() section of my code
if ((PowersScreen3.getDecodedChoice() == Assets.powerup_speedup && System.currentTimeMillis() - power_start_3 <= 10000) ||
(PowersScreen2.getDecodedChoice() == Assets.powerup_speedup && System.currentTimeMillis() - power_start_2 <= 10000) ||
(PowersScreen.getDecodedChoice() == Assets.powerup_speedup && System.currentTimeMillis() - power_start <= 10000)) {
//The above if-state is just checking the timing system and deciding which powerup was chosen
PowerConstants.popcorn_speed = 4;
Log.d("Powerups", "all popcorn speed up");
}
It makes sense that by changing the speed constant, the speed of the newly formed popcorn would change. However, I also want the current popcorn in the arraylist to change their speed as well. How can I do this?
Related
I am making a pong type game in java and I am trying to make the ball bounce off of the walls but whenever the ball hits the ball it just stops, it does not reflect off of the wall and I can't seem to figure out why.
Ball class which handles the ball movement
public class Ball {
private double x;
private double y;
private double time;
private double xreflection=1.0;
private double yreflection=1.0;
private BallTrajectory traj=new BallTrajectory(20, 20);
public Ball(double x, double y) {
this.x=x;
this.y=y;
}
public void tick() {
time+=1.0/60.0;
if(x==0)
xreflection=1.0;
else if(x==Game.Width-15)
xreflection=-1.0;
if(y==0)
yreflection=1.0;
else if(y==Game.Height-15)
yreflection=-1.0;
x+=traj.xvel()*xreflection;
y-=traj.yvel(time)*yreflection;
}
public void render(Graphics g) {
g.setColor(Color.pink);
g.fillOval((int)x, (int)y, 15,15);
}
}
This class handles the trajectory of the ball as it moves in projectile type motion
public class BallTrajectory {
private double initvel;
private double theta;
public BallTrajectory(double initvel, double theta) {
this.initvel=initvel;
this.theta=theta;
}
public double xvel() {
double xvelo=initvel*Math.cos(Math.toRadians(theta));
return xvelo;
}
public double yvel(double time) {
double yvelo=initvel*Math.sin(Math.toRadians(theta))-(9.8*time);
return yvelo;
}
public double xpos(double time) {
double xpos=initvel*Math.cos(Math.toRadians(theta))*time;
return xpos;
}
public double ypos(double time) {
double ypos=initvel*Math.sin(Math.toRadians(theta))*time-.5*9.8*Math.pow(time, 2);
return ypos;
}
Without going through a whole bunch of testing, I would suggest that it is very unlikely that x will ever be exactly equal to Game.Width or 0. Instead, you should be testing that the value is "within bounds" instead, maybe something like...
public void tick() {
time += 1.0 / 60.0;
if (x <= 0) {
xreflection = 1.0;
} else if (x >= Game.Width - 15) {
xreflection = -1.0;
}
if (y <= 0) {
yreflection = 1.0;
} else if (y >= Game.Height - 15) {
yreflection = -1.0;
}
x += traj.xvel() * xreflection;
y -= traj.yvel(time) * yreflection;
}
You should also start taking the time to learn how to debug your code, it is something you will need to do a lot, from desk-checking your logic to using print statements and the debugger
I'm trying to make a random map generator. It should create a random sized rooms at random coordinates, and remove the room it it overlaps with other rooms. However, the overlap checking isn't working. Here are the relevant parts of code:
public static void generateMap() {
rooms[0] = new Room(0,10,10,5); // For some reason doesn't work without this?
for (int i=0;i<ROOMS;i++) {
int x = randomWithRange(0,WIDTH);
int y = randomWithRange(0,HEIGHT);
int height = randomWithRange(MINROOMSIZE,MAXROOMSIZE);
int width = randomWithRange(MINROOMSIZE,MAXROOMSIZE);
while (x+width > WIDTH) {
x--;
}
while (y+height > HEIGHT) {
y--;
}
Room room = new Room(x,y,width,height);
if (room.overlaps(rooms) == false) {
rooms[i] = room;
}
}
}
And then the Room class:
import java.awt.*;
public class Room {
int x;
int y;
int height;
int width;
public Room(int rx, int ry, int rwidth, int rheight) {
x = rx;
y = ry;
height = rheight;
width = rwidth;
}
boolean overlaps(Room[] roomlist) {
boolean overlap = true;
Rectangle r1 = new Rectangle(x,y,width,height);
if (roomlist != null) {
for (int i=0;i<roomlist.length;i++) {
if (roomlist[i] != null) {
Rectangle r2 = new Rectangle(roomlist[i].x,roomlist[i].y,roomlist[i].width,roomlist[i].height);
if (!r2.intersects(r1) && !r1.intersects(r2)) {
overlap = false;
}
else {
overlap = true;
}
}
}
}
return overlap;
}
}
So I've been testing this, and it removes a few rooms each time, but there's always some that are overlapping depending on the number of rooms of course. There must be some stupid easy solution I just can't see right now... Also, why doesn't it generate any rooms unless I manually add the first one? Thanks
Your problem is this part of overlaps function:
overlap = false;
What is happening in your code is that you keep checking rooms if they overlap or not, but if you find one which overlaps, you keep going. And then when you find a room which does not overlap, you reset the flag. Effectively the code is equivalent with just checking the last room.
Remove the overlap flag completely. Instead of overlap = true; statement put return true; (because at this point we know that at least one room is overlapping). Don't do anything when you find out that the room is not overlapping with other room (in the for cycle). At the end, after the for cycle just return false; Fact that code execution got to that point means there is no overlapping room (otherwise it would have just returned already)
Note: I believe that condition !r2.intersects(r1) && !r1.intersects(r2) is redundant. .intersects(r) should be commutative, meaning that that r1.intersects(r2) and r2.intersects(r1) give the same results.
For your first issue where you have initialized first room, you don't have to do that.
rooms[0] = new Room(0,10,10,5); // For some reason doesn't work without this?
You just need to check for first room and no need to check for overlap as it is the first room.
For the second issue, you can return true at the first time you find an intersect otherwise return false at the end of the loop.
Code for your reference.
class Room {
int x;
int y;
int height;
int width;
public Room(int rx, int ry, int rwidth, int rheight) {
x = rx;
y = ry;
height = rheight;
width = rwidth;
}
boolean overlaps(Room[] roomlist) {
Rectangle r1 = new Rectangle(x, y, width, height);
if (roomlist != null) {
for (int i = 0; i < roomlist.length; i++) {
if (roomlist[i] != null) {
Rectangle r2 = new Rectangle(roomlist[i].x, roomlist[i].y, roomlist[i].width, roomlist[i].height);
if (r2.intersects(r1)) {
return true;
}
}
}
}
return false;
}
}
public class RoomGenerator {
private static final int ROOMS = 10;
private static final int WIDTH = 1200;
private static final int HEIGHT = 1000;
private static final int MINROOMSIZE = 10;
private static final int MAXROOMSIZE = 120;
public static void main(String[] args) {
generateMap();
}
public static void generateMap() {
Room rooms[] = new Room[10];
for (int i = 0; i < ROOMS; i++) {
int x = randomWithRange(0, WIDTH);
int y = randomWithRange(0, HEIGHT);
int height = randomWithRange(MINROOMSIZE, MAXROOMSIZE);
int width = randomWithRange(MINROOMSIZE, MAXROOMSIZE);
while (x + width > WIDTH) {
x--;
}
while (y + height > HEIGHT) {
y--;
}
Room room = new Room(x, y, width, height);
if( i ==0)
{
rooms[0] = room;
}else if (room.overlaps(rooms) == false) {
rooms[i] = room;
}
}
}
private static int randomWithRange(int min, int max) {
// TODO Auto-generated method stub
Random r = new Random();
return r.nextInt((max - min) + 1) + min;
}
}
public class Pond {
public static void allcreationco(){
for (int i = 0; i < 100; i++){
int radius = 100;
int x = (int) (Math.random() * 2 * radius - radius);
int ylim = (int) Math.sqrt(radius * radius - x * x);
int y = (int) (Math.random() * 2 * ylim - ylim);
Fish.xfishc.add((int) x);
Fish.yfishc.add((int) y);
}
allcreationdir();
}
public static void allcreationdir(){
for (int i = 0; i < Fish.xfishc.size(); i++){
double radius = Math.random()*1;
double angle = Math.random()*2*Math.PI;
double x = Math.cos(angle)*radius + 0;
if (x > 0){
Fish.xfishcb1.add(true);
}
else {
Fish.xfishcb1.add(false);
}
}
for (int i = 0; i < Fish.yfishc.size(); i++){
double radius = Math.random()*1;
double angle = Math.random()*2*Math.PI;
double x = Math.cos(angle)*radius + 0;
if (x > 0){
Fish.yfishcb1.add(true);
}
else {
Fish.yfishcb1.add(false);
}
}
Hi, my objective is to create a simulation (no visual drawing needed, just something to easily print info about) of a circular pond with fish randomly swimming in it. The code above is a way of initiating 100 hypothetical fish into Arraylists in the form of x and y coordinates based on a hypothetical circle with a radius of 100 (there's gotta be a better way to do this). I would like to have each of the 100 fish be able to swim in random directions and change to new random directions every time they reach the end of the pond. Maybe I'd like them to reproduce after a certain time, or include another fish that moves in straight lines and can eat another fish.
public class Salmon extends Fish {
public static int salmonre = 0;
public static void salmonmove(){
for (int i = 0; i < xfishc.size(); i++){
if (xfishcb1.get(i) == true){
int d = xfishc.get(i);
int e = d + 1;
xfishc.set(i , e);
}
else{
int d = xfishc.get(i);
int e = d - 1;
xfishc.set(i , e);
}
}
for (int i = 0; i < yfishc.size(); i++){
if (yfishcb1.get(i) == true){
int d = yfishc.get(i);
int e = d + 1;
yfishc.set(i , e);
}
else{
int d = yfishc.get(i);
int e = d - 1;
yfishc.set(i , e);
}
}
salmonre++;
}
}
I also used Boolean arraylists to randomly determine what directions the fish are supposed move in. Please be gentle with me in your rhetoric because I'm well aware that my approaches are ridiculous. I know it's best to use trigonometry when simulating objects and their behaviors in a circle, but for some reason, I'm not able to wrap my head around this when looking on the internet (I keep finding things more complicated that involve visual illustrations). Could you "please" give me comprehensive answers with ideas? I'm frustrated.
I wasn't entirely able to figure out how you wanted your Fish class to work based on your code, but some tips:
In Object Oriented programming, you do not want to have a class Fish that has static methods for updating two lists containing X and Y coordinates for all the fish.
Instead, you want an object of class Fish to represent everything about a single fish. You can then have a list of Fish objects.
A pair of booleans is really too coarse for directions. Use a double instead, one for each fish (stored in the Fish instance).
To implement the direction changing behavior, just check whether the next move would move the fish out of the water, and if so, pick a different direction.
Here's a simple, self contained example of the above for two Fish. They start out together and in the same direction, but diverge when they hit the edge and swim in different, random directions:
class Fish {
private double x, y;
private double angle, speed;
public Fish() {
x = y = angle = 0;
speed = 5;
}
void move() {
// If we swim at this angle, this is where we'll end up
double newX = x + Math.cos(angle) * speed;
double newY = y + Math.sin(angle) * speed;
if (isInPond(newX, newY)) {
// That's still in the pond, go there
x = newX;
y = newY;
} else {
// That's outside the pond, change direction
angle = Math.random() * 2 * Math.PI;
}
}
public String toString() {
return String.format(
"Position: %.0f,%.0f. Angle: %.0f degrees.",
x, y, angle * 180/Math.PI);
}
// Check whether some coordinates are within a circular pond with radius 100
static boolean isInPond(double x, double y) {
return Math.sqrt(x*x+y*y) < 100;
}
public static void main(String[] args) throws Exception {
Fish nemo = new Fish();
Fish marlin = new Fish();
while(true) {
nemo.move();
marlin.move();
System.out.println("Nemo: " + nemo);
System.out.println("Marlin: " + marlin);
Thread.sleep(500);
}
}
}
So we have to make a ball bounce with the code given. Basically the translate method below is run every 1 second because of a timer method in the tester. Also the tester passes dx as 0 and dy as 1. Initially the ball moves down and I am trying to make it bounce back up once it reaches y = 100. I can't figure out why this code would not work because logically it makes sense to me...
public void translate(int dx, int dy) {
x += dx;
y += dy;
if(y >= 100){
dy = -1 * dy;
}
}
I run it with this code, the ball keeps moving down.
y = y direction of the ball and x = x direction of the ball
Update: Thanks for the answers. So from what I am getting I need to add the if statements inside the method that calls the translate method. The code for the method calling translate is:
private void moveMyShape() {
Timer t = new Timer(DELAY, getActionListener());
t.start();
} //method
private ActionListener getActionListener() {
return new
ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
myMoveableShape.translate(0, 1);
myShape.repaint();
}
};
}
So how would I go about adding if statements in this method? Like how can I keep track of the y position of the ball so I can add the if statements above the translate method. By the way this actionListener code is in a different class. It is in a tester class.
2nd Update: Yes, I have a public static int getY() inside the myMoveableShape. In the tester I added this code:
public void actionPerformed(ActionEvent ae) {
if(MyMoveableShape.getY() > 100 || MyMoveableShape.getY() < 0){
myMoveableShape.translate(0,-1);
}
myMoveableShape.translate(0,1);
myShape.repaint();
But the ball just gets to y = 100 position and stops moving.
When you pass the int into the translate method it is 'by value', not 'by reference' so any changes you make to the value will not last beyond the scope of the method.
You need to do the the check of y, and the reversal of it, higher up in your code, likely in the same method where you are calling translate from.
Update:
Add a 'velocity' property to your class, something like
private int velocityY = 1;
public int getVelocityY() {
return velocityY;
}
public void setVelocityY(int vel) {
velocityY = vel;
}
And then you can modify that block to be something similar to
public void actionPerformed(ActionEvent ae) {
if(MyMoveableShape.getY() > 100 || MyMoveableShape.getY() < 0){
myMoveableShape.setVelocityY(-myMoveableShape.getVelocityY());
}
myMoveableShape.translate(0,myMoveableShape.getVelocityY());
myShape.repaint();
}
Update 2: Based on your comments, you could give this a whirl:
private boolean goingUp = false;
public void translate(int dx, int dy) {
x += dx;
if(goingUp){
y += dy;
} else {
y -= dy;
}
if(y >= 100 || y < 0){
goingUp = !goingUp; //Toggle it back and forth
}
}
try putting your if statement above the += statement.
Like:
public void translate(int dx, int dy)
{
if(y >= 100)
{
dy = -1 * dy;
}
x += dx;
y += dy;
}
I'm sure i'm missing something like are y and x static? need more code to do any better.
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;
}
}