Apologies if the question isn't clear but I couldn't think of another way to phrase it.
This is for a class assignment which I've been working at in BlueJ all weekend. I have to change a method (bounce) to let a user choose how many balls should be bouncing.
Other requirements are: the balls should be of different sizes and should be displayed in a row along the top of the screen BEFORE they bounce.
In order to do this I have to use a collection (ArrayList, HashMap, HashSet). So far I've used HashMap and have been able to have the user choose a number of "balls" of random sizes which place themselves in random positions in the top half of the screen.
When I try to have each ball bounce from its position at the top of the screen, ending at the right hand side I come up stuck. I can make the code draw one ball, bounce it then draw another ball, bounce it etc until the user selected number of balls has looped round.
There are two other classes, one to draw the canvas and one to draw the balls and move them. Both of which I'm not allowed to touch.
What I'm doing the wrong way is probably right in front of me but i've been staring at this code so long I thought I'd ask.
My current version of the code looks like this:
import java.awt.Color;
import java.util.HashMap;
import java.util.Random;
import java.util.Iterator;
public class BallDemo
{
private Canvas myCanvas;
private HashMap<Integer, BouncingBall> ballMap;
private int n;
private int j;
private BouncingBall ball;
/**
* Create a BallDemo object. Creates a fresh canvas and makes it visible.
*/
public BallDemo()
{
myCanvas = new Canvas("Ball Demo", 600, 500);
}
And the method I have to edit to bounce the balls:
public void bounce(int numBalls)
{
ballMap = new HashMap<Integer, BouncingBall>();
int ground = 400; // position of the ground line
Random randomD1 = new Random();
Random xpos = new Random();
myCanvas.setVisible(true);
// draw the ground
myCanvas.drawLine(50, ground, 550, ground);
// add balls to HashMap
for(n = 0; n < numBalls; n++) {
ballMap.put(numBalls, (ball = new BouncingBall(xpos.nextInt(300), 50, randomD1.nextInt(200), Color.BLUE, ground, myCanvas)));
//
for(j= 0; j < ballMap.size(); j++) {
ball.draw();
boolean finished = false;
while(!finished) {
myCanvas.wait(50); // small delay
ball.move(); // bounce the ball
// stop once ball has travelled a certain distance on x axis
if(ball.getXPosition() >= 550) {
finished = true;
}
}
}
}
}
}
Am I even on the right lines using a HashMap? The combination of keys, values seemed the best way to go. I think I need to somehow iterate through the items placed in the collection to make them bounce using the move() method. But first I need the balls to stay in a row at the top of the screen, no matter how many the user defines.
I'm new to programming and I'm just coming up stumped.
Thanks for any help!
#16dots is partly right, except ballMap.put(numBalls, ball); will over write the same value in the hash map each time, as numBalls does not change...
The key should be unique.
It should read...
for (int n; n < numBalls; n++) {
BouncingBall ball = new BouncingBall(xpos.nextInt(300), 50,
randomD1.
nextInt(200), Color.BLUE, ground, myCanvas);
ballMap.put(n, ball);
}
boolean finished = false;
while (!finished) {
finished = true;
for (int j = 0; j < ballMap.size(); j++) {
BouncingBall selectedBall = ballMap.get(j);
selectedBall.draw();
// Only move the ball if it hasn't finished...
if (selectedBall.getXPosition() < 550) {
selectedBall.move(); // bounce the ball
// stop once ball has travelled a certain distance on x axis
if (selectedBall.getXPosition() < 550) {
finished = false;
}
}
}
myCanvas.wait(50); // small delay
}
Related
I'm trying to make a grid of squares that change their fill (from black to white and vice-versa) when clicked. I'm able to turn the entire grid on or off currently, but I'm unable to figure out how to specify which particular square should be toggled when the mouse clicks within its borders. I've created buttons using mouseX and mouseY coordinates before, but they were for specific objects that I could adjust manually. I can't figure out how to do this using for loops and arrays.
I've been told to create a boolean array and pass the value of that array to the grid array, but again, I don't know how to specify which part of the array it needs to go to. For example, how do I change the fill value of square [6][3] upon mousePressed?
Here is my code so far:
int size = 100;
int cols = 8;
int rows = 5;
boolean light = false;
int a;
int b;
void setup() {
size (800, 600);
background (0);
}
void draw() {
}
void mousePressed() {
light = !light;
int[][] box = new int[cols][rows];
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
box[i][j] = i;
int a = i*100;
int b = j*100;
if (light == true) {
fill(255);
} else {
fill(0);
}
rect(a, b, 100, 100);
println(i, j);
}
}
}
First of all, you are currently recreating the entire board whenever the mouse is pressed. You must retain that info between mouse clicks, so make box a global array up there with the others. Further, it's sufficient to make it a boolean array if all you care about is the on/off state of each square:
boolean[][] isSquareLight = new boolean[cols][rows];
Instead of
if (light == true) {
you should then just check
if (isSquareLight[i][j] == true) {
(note that the == true is redundant).
Now, you've already written code that finds the coordinates for each box: You're passing it to rect!
rect(a, b, 100, 100);
All that is left to do is check whether the mouse is inside this rect, i.e. whether mouseX is between a and a+100 (and similar for mouseY) - if that's the case, then the user clicked in the box given by the current (i, j), so you can just negate isSquareLight[i][j] (before checking it like above) and it will work.
There are ways to calculate this without looping through the entire grid every time, but maybe the above helps you find the path yourself instead of just getting the code made for you.
PS: The int a; int b; at the top does nothing and can be removed. You are using the local variables a and b in your function, which is correct.
I am trying to write a small program that has a given number of balls (in the example code below it's 3) travel back and forth across the screen at different speeds and phases (start offset).
This much has been achieved in the code. Although I want to be able to select the balls (one at a time) using a mouse click.
I have used the word "HIT!!!" to signify in the console that a ball has been clicked.
My problem is that when I run the code below, I only get a "HIT!" in the console when I click the top ball. That is when the first element y[0] matches with the click_Y variable. When I am sure (but obviously mistaken somehow) that there should be matches when I click in the vicinity of y[1] & y[2].
I'd really be grateful for any help with these. As it's gotten to the point where I am starting to stare blankly at the screen. Thanks.
int noCircles; // the number of items in the array (# of circles)
float[] y; // y-position of each circle (fixed)
float[] speed; // speed of each circle
float[] phase; // phase of each circle
float red = 120;
float green = 120;
float blue = 120;
float click_X;
float click_Y;
void setup() {
size(500, 500);
noCircles = 3;
// allocate space for each array
y = new float[noCircles];
speed = new float[noCircles];
phase = new float[noCircles];
// calculate the vertical gap between each circle based on the total number
// of circles
float gap = height / (noCircles + 1);
//setup an initial value for each item in the array
for (int i=0; i<noCircles; i++) {
y[i] = gap * (i + 1);
// y is constant for each so can be calculated once
speed[i] = random(10);
phase[i] = random(TWO_PI);
}
}
void draw() {
background(155);
for (int i=0; i<noCircles; i++) {
// calculate the x-position of each ball based on the speed, phase and
//current frame
float x = width/2 + sin(radians(frameCount*speed[i] ) + phase[i])* 200;
if (dist(x, y[i], click_X, click_Y) <= 20){
println("HIT!!!!!!!!!!!!!!!!!!");
}
ellipse(x, y[i], 20, 20);
click_X = 0;
click_Y = 0;
}
}
void mousePressed() {
println("You clicked******************************************");
click_X = mouseX;
click_Y = mouseY;
println("click_X =" + click_X);
println("click_Y =" + click_Y);
}
Problems like these are best solved by debugging your program. Start by tracing through the code by hand, then add print statements (more than you've already added), and if that doesn't work then don't be afraid to use the debugger.
You're using the click_X and click_Y variables to check the position of the mouse against the position of each ball. Trace through the for loop in your draw() function. What happens at the end of the first iteration?
You reset the values of click_X and click_Y. That's why you aren't detecting any hits on the other circles.
You could probably refactor your code to only reset those variables if something has been hit, but really, I would stop using them altogether.
I'm guessing that you're using those variables because you only want to check when the mouse is pressed? Just use the mousePressed variable for that. Then you can use the mouseX and mouseY variables directly.
Then your if statement would look like this:
if (mousePressed && dist(x, y[i], mouseX, mouseY) <= 20) {
println("HIT: " + i);
}
Also, using separate arrays like this is called parallel arrays, and is general a bad habit to get into. You should probably use classes instead.
So i am trying to create a game that is top down 2d and using a pixel tiled map style format. They way i would like to do it is have say 4 different tiles, different shades of green and brown for grass.
I would like to generate these 4 tiles randomly around the 1920*1080 pixel area to give a semi realistic effect.
So something like this:
But with randomly generated different tiles in there.
What would be the best way of doing this?
Just use a for loop and place a random tile at each location
for(int i = 0; i < numTilesY; i++)
{
for(int j = 0; j < numTilesX; j++)
{
placeRandomTile(i, j);
}
}
placeRandomTile would just use java's Random class to choose a random number, and depending on the number will place the corresponding tile. For example, if randomNumber == 2, place grass tile.
If you want the tiles to form a realistic looking world similiar to Minecraft, you should use some sort of a noise function like Perlin Noise. It will be random in that every time you generate a new world, the world will be different.
EDIT:
int[][] map= new int[32][32]; //create 32x32 tile map
public void placeRandomTile(int xIndex, int yIndex)
{
Random rand = new Random();
int value = rand.nextInt(5);//get value between 0 and 5
int tile = 0;
if(value == 0)
{
tile = 1; //1 corresponds to GRASS tile
}
if(value == 1)
{
tile = 2; //WATER tile
}
this.map[xIndex][yIndex] = tile;
}
Now that you have your map data stored in a 2D array, you just need to loop through the data and draw each tile accordingly at the proper locations.
In your drawing method, you would just draw each tile from the map like so:
public void draw(float x, float y, Graphics g)
{
for(int i = 0; i < map.length; i++)
{
for(int j = 0; j < map[i].length; j++)
{
int tileType = map[i][j]; //get tile type
if(tileType == Tile.grass.id)
{
Tile.grass.draw(x + (j * Tile.WIDTH), y + (i * Tile.HEIGHT));//draw grass
}
}
}
}
Since you are using libgdx, you will need to look into these classes: TiledMap, TiledMapTileLayer, Screen, Texture, and OrthogonalTiledMapRenderer. You basically create a TiledMap and draw Textures using a TiledMapRenderer.
You can view a sample game I have on Github from a while ago that does what you want. It's here. Look at Dungeon class and screens/GameScreen class
I am trying to make a multiple enemy array, where every 30 secods a new bullet comes from a random point. And if the bullet is clicked it should disapear and a pop like an explosion should appear. And if the bullet hits the ball then the ball pops.
so the bullet should change to a different sprite or texture. same with the ball pop.
But all that happens is the bullet if touched pops and nothing else happens. And if modified then the bullet keeps flashing as the update is way too much.
I have added COMMENTS in the code to explain more on the issues.
below is the code.
if more code is needed i will provide.
Thank you
public class GameRenderer {
private GameWorld myWorld;
private OrthographicCamera cam;
private ShapeRenderer shapeRenderer;
private SpriteBatch batcher;
// Game Objects
private Ball ball;
private ScrollHandler scroller;
private Background background;
private Bullet bullet1;
private BulletPop bPop;
private Array<Bullet> bullets;
// This is for the delay of the bullet coming one by one every 30 seconds.
/** The time of the last shot fired, we set it to the current time in nano when the object is first created */
double lastShot = TimeUtils.nanoTime();
/** Convert 30 seconds into nano seconds, so 30,000 milli = 30 seconds */
double shotFreq = TimeUtils.millisToNanos(30000);
// Game Assets
private TextureRegion bg, bPop;
private Animation bulletAnimation, ballAnimation;
private Animation ballPopAnimation;
public GameRenderer(GameWorld world) {
myWorld = world;
cam = new OrthographicCamera();
cam.setToOrtho(true, 480, 320);
batcher = new SpriteBatch();
// Attach batcher to camera
batcher.setProjectionMatrix(cam.combined);
shapeRenderer = new ShapeRenderer();
shapeRenderer.setProjectionMatrix(cam.combined);
// This is suppose to produce 10 bullets at random places on the background.
bullets = new Array<Bullet>();
Bullet bullet = null;
float bulletX = 00.0f;
float bulletY = 00.0f;
for (int i = 0; i < 10; i++) {
bulletX = MathUtils.random(-10, 10);
bulletY = MathUtils.random(-10, 10);
bullet = new Bullet(bulletX, bulletY);
AssetLoader.bullet1.flip(true, false);
AssetLoader.bullet2.flip(true, false);
bullets.add(bullet);
}
// Call helper methods to initialize instance variables
initGameObjects();
initAssets();
}
private void initGameObjects() {
ball = GameWorld.getBall();
bullet1 = myWorld.getBullet1();
bPop = myWorld.getBulletPop();
scroller = myWorld.getScroller();
}
private void initAssets() {
bg = AssetLoader.bg;
ballAnimation = AssetLoader.ballAnimation;
bullet1Animation = AssetLoader.bullet1Animation;
ballPopAnimation = AssetLoader.ballPopAnimation;
}
// This is to take the bullet away when clicked or touched.
public void onClick() {
for (int i = 0; i < bullets.size; i++) {
if (bullets.get(i).getBounds().contains(0, 0))
bullets.removeIndex(i);
}
}
private void drawBackground() {
batcher.draw(bg1, background.getX(), background.getY(), background.getWidth(), backgroundMove.getHeight());
}
public void render(float runTime) {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL30.GL_COLOR_BUFFER_BIT);
batcher.begin();
// Disable transparency
// This is good for performance when drawing images that do not require
// transparency.
batcher.disableBlending();
drawBackground();
batcher.enableBlending();
// when the bullet hits the ball, it should be disposed or taken away and a ball pop sprite/texture should be put in its place
if (bullet1.collides(ball)) {
// draws the bPop texture but the bullet does not go just keeps going around, and the bPop texture goes.
batcher.draw(AssetLoader.bPop, 195, 273);
}
batcher.draw(AssetLoader.ballAnimation.getKeyFrame(runTime), ball.getX(), ball.getY(), ball.getWidth(), ball.getHeight());
// this is where i am trying to make the bullets come one by one, and if removed via the onClick() then bPop animation
// should play but does not???
if(TimeUtils.nanoTime() - lastShot > shotFreq){
// Create your stuff
for (int i = 0; i < bullets.size; i++) {
bullets.get(i);
batcher.draw(AssetLoader.bullet1Animation.getKeyFrame(runTime), bullet1.getX(), bullet1.getY(), bullet1.getOriginX(), bullet1.getOriginY(), bullet1.getWidth(), bullet1.getHeight(), 1.0f, 1.0f, bullet1.getRotation());
if (bullets.removeValue(bullet1, false)) {
batcher.draw(AssetLoader.ballPopAnimation.getKeyFrame(runTime), bPop1.getX(), bPop1.getY(), bPop1.getWidth(), bPop1.getHeight());
}
}
/* Very important to set the last shot to now, or it will mess up and go full auto */
lastShot = TimeUtils.nanoTime();
}
// End SpriteBatch
batcher.end();
}
}
Thank you
Hmm...why are you drawing graphics from inside of the if where you are adding new bullets? This way all you draw will be drown only one frame per 30 seconds. Inside that if you should only add/remove objects and draw them outside, all the time. No drawing inside that if!
In addition to MilanG's answer
The bullets.get(i); line does nothing.. You'll want to store the returned Bullet into a variable, for which it seems you created the bullet1 var.
Also, you really shouldn't add elements to or remove elements from an array while looping through it. Consider using a second array for elements to be added/removed and use that to alter the main array or use an iterator.
[edit]
In this particular case you could also do something like this, though it would only work for one bullet per click
int index = -1;
for (int i = 0; i < bullets.size; i++) {
if (bullets.get(i).getBounds().contains(0, 0)) {
index = i;
break;
}
}
if(index > -1) bullets.removeIndex(index);
It also seems your .contains() should be passed the clicked position instead of 0,0?
So I'm working on a collision detection code and what i do is when the user rectangle overlaps the rectangle where they cant move, I stop them from moving. So if im moving right and I hit a wall i cant move forward. This works. However if after i hit that wall, I want to move down or up form that point, I get stuck.
This is how i check if the user has colidded
private void checkCollision() {
for (int x = 0; x < amount; x++) {
if (collsionRect[x].overlaps(user)) {
Gdx.app.log(ChromeGame.LOG, "Overlap");
xD = 0;
yD = 0;
}
}
}
And this is how I move my user
private void moveUser() {
// camera.translate(xD, yD);
player.translate(xD, yD);
camera.position.set(player.getX(), player.getY(), 0);
// Gdx.app.log(ChromeGame.LOG, player.getX() + "," + player.getY());
user = new Rectangle(player.getX(), player.getY(), player.getWidth(),
player.getHeight());
checkCollision();
}
In my render method I keep calling the move userMove method till I let go of the keyboard wher eit turns the xD,yD to zero
The problem is that you check if rectangles overlap. This means that your character is not colliding unless some part of him is already inside the wall, then he stops moving. The next time you want to move the character he is already colliding (a part of him is inside the wall) and so checkCollision() will set xD, yD to 0 and your character won't move.
The easiest solution to this is to make a "fake" move (interpolation) and check if this new move will make him collide, if he will collide you simply don't accept this new move. In pseudocode
new position = player.x + deltaX, player.y+deltaY
create new rectangle for player from new position
check collision with new rectangle
if not collide player.x += deltaX, player.y+=deltaY