I have built a simple program to draw a city skyline within a frame with some random elements. The number of buildings will be a random number between [5 - 9], the width and height of each building are also random (within a given range) as is the gap between each building. The problem I am having comes when I try to select a different gap range depending on the amount of buildings. Here is the the section of code in question (I have imported java.util.*;):
Random gen = new Random();
int buildNum = gen.nextInt(5) + 5; //generate random number [5 - 9]
for (int i = 0; i < buildNum; i++)
{
//generate random width
buildWidth = gen.nextInt(26) + 35; //[35 - 60]
//generate random height
buildHeight = gen.nextInt(201) + 200; //[200 - 400]
//instantiate building
building = new Building (gap, 500 - buildHeight, buildWidth, buildHeight, Color.gray);
buildingsArray.add(building);
//draw building
buildingsArray.get(i).draw (page);
//generate random gap between building
if (buildNum < 7)
{
gap += buildWidth + gen.nextInt(16) + 40;
}
if (buildNum == 7)
{
gap += buildWidth + gen.nextInt(9) + 15;
}
if (buildNum > 7)
{
gap += buildWidth + gen.nextInt(9) + 8;
}
}
The buildNum variable is initialized with a random number, then that number is used to iterate through. The issue is that when I check the buildNum variable in the three ifs at the bottom, the output does not seem to correspond with what I would expect. I will get 5 buildings with very small gaps (as though it passed the buildNum > 7 test), or I will get 7 buildings with the first six normal, but a very small gap before the seventh building. I'm sure it's something very simple that I am overlooking, but I have been starting at this for hours and tried many different things (including testing buildNum at fixed values 5, 6, 7, 8, or 9, and putting an else clause instead of a third if.) Ultimately, I would like the program to pick a number of buildings between 5 and 9 and then scale the gaps accordingly. I tried factoring in both buildNum and buildWidth, but I couldn't get it to work and settled for this. If anyone has a better way or can shed some light on my problem, I would greatly appreciate it!
Update: I have tested with a constant number of buildings to see what the range of output looks like. The problem is that I would be expecting 5 or 6 buildings to have wide gaps due to the if (buildNum < 7) condition and be progressively smaller due to the other 2 conditions. Instead, I end up with 5 buildings having really really small gaps (6 buildings is fine) and seven and 8 buildings going off the page (even though I've done the math and the gap that is added within the if conditionals would not allow this). There are some helpful comments here already, to answer your questions: Yes, the gap is an absolute position from the left. A small gap is initialized so that the first building does not touch the left side of the frame. Then, buildings are instantiated one at a time with a gap size chosen from those if statements. The position of the next building's left side is determined from a compounding gap variable that includes all previous gaps and all previous building widths. I am especially curious about Lee's comment that the if (buildNum ==7) and (buildNum>7) clauses might never happen. Could you explain that a little further? Wouldn't 8 or 9 satisfy the >7 condition and 7 satisfy the ==7 condition? if not, what can I do to change it? also, here is my constructor:
/**
* Constructor: sets background color and frame size
*/
public SkylinePanel()
{
setPreferredSize (new Dimension(600, 500));
setBackground (Color.black);
}
There is not much to it, the real work (including the above shown for loop) is within paintComponent and I've shown everything that is related to the building gaps already. Here is the draw method (it's in my building class):
/**
* Draws a building or window with the specified parameters
*/
public void draw (Graphics page)
{
page.setColor (color);
page.fillRect (x, y, width, height);
winNum = gen.nextInt(8) + 2;
//generate a random number [2 - 9] of windows to draw on a given building
for (int i = 0; i < winNum; i++)
{
page.setColor (Color.yellow);
page.fillRect (gen.nextInt(width - WIN_X) + x, gen.nextInt(height - WIN_Y) + y, WIN_X, WIN_Y);
}
}
Ignore the code about windows, that works fine. I added that after I decided to move on from the gap problem for the time being.
Oh, and just in case anyone needs to see it, here is the constructor of the Building class:
public Building (int upperX, int upperY, int buildWidth, int buildHeight, Color shade)
{
x = upperX;
y = upperY;
width = buildWidth;
height = buildHeight;
color = shade;
}
Any other thoughts or questions would be greatly appreciated!
Related
I'm doing a little personnal project to improve my java skill.
What I do there, is that i create 3 JPanel (a global one, and 2 into the global).
In one of them (the right one), I want to draw blocks. Those blocks have a random height, width and an unique id.
In one of my java class, i create a random number of blocks. Then, I'm doing a function to draw those blocks and here are the problems.
First of all, the paintComponent function is executing twice, and I only want it to be executed one time.
Secondly, my blocks are.. not draw very well.. here is a picture of 20 blocks drawing into my panel (to debug, I put a fix number of blocks). My windows is 900x700.
Here is my paintComponent function, I tried to see where I did a mistake, but
I pull my hair off..
Your target picture indicates you basically want to render the images in lines, i.e. if an image wouldn't fit into the current line you'd start a new one. Thus you need to track the offsets for x and y as well as the hight of the highest block in the line. This means for each block you'd do something like this (untested but a little debugging should help if this isn't 100% correct):
//maximum width of a line
int maxWidth = 300;
//Space in pixels between the border as well as the blocks
int paddingX = 5;
int paddingY = 5;
//the offset for the next block
int offsetX = paddingX;
int offsetY = paddingY;
int currentLargestHeight = 0;
for( Block block : blocks ) {
//if the block doesn't fit into the "line" start a new one
//we assume an empty line always can take at least one block
if( block.getWidth() > (maxWidth - offsetX - paddingX) ) {
//advance down
offsetY += currentLargestHeight + paddingY;
//we have a new line so the current largest height is 0 again
currentLargestHeight = 0;
//start left again
offsetX = paddingX;
}
//render the block
g2.fillRect(offsetX, offsetY, block.getWidth(), block.getHeight());
offsetX += block.getWidth() + paddingX;
if( block.getHeight() > currentLargestHeight ) {
currentLargestHeight = block.getHeight();
}
}
I am in my second year of programming, and only know Java. However for my class, we have to create a vertical side-scroller. My set-up is that I have an ArrayList for enemy ships, and an ArrayList for player rockets. I draw the 2 rockets in precise spots on the player ship in the render(Graphics2D g) method, and the enemy ships in the same method. The rocket ArrayList is populated by
arsenal.add(r);
arsenal.add(r2);
in which r and r2 are rockets with the parameters (x,y,w,h), and arsenal is the rocket arraylist. I have set them to be drawn in very specific places, at the different ends of the spaceship. In the update() method, I have this method:
if (arsenal.size() < 0){
arsenal.add(new Rocket(p.currentX(), p.getHalfY(),30,30));
arsenal.add(new Rocket(p.getHalfX(), p.getHalfY(), 30, 30));
}
This is to make sure that when the arraylist gets empty, the rockets are re-added and redrawn to the coordinates that correspond to the ends of the spaceship. The only way the rockets disappear is when they go off-screen, or collide with the enemy. I have put this code in to check for that:
for(int x = army.size()-1; x >= 0; x--){
if (army.get(x).getR().y > 1024){
army.remove(x);
}
if(p.intersects(army.get(x).getR())){
army.remove(x);
p.changeH(10);
}
for (int q = arsenal.size()-1; q>=0;q++){
if (arsenal.get(q).intersects(army.get(x).getR())){
score+=20;
army.remove(x);
arsenal.remove(q);
}
}
}
This whole method goes through the enemy ship arraylist "army" and checks to see if (1) the enemy ship has gone past the set height, (2) if the player ship collides with the enemy ship, and (3) if a rocket in the arsenal arraylist has collided with an enemyship. All collisions are done using rectangles. The p.changeH(10); command is to lower the player's health by 10, and the score +=20; is to increase the score. For all these checks, I make sure that the enemyship removes itself from the arraylist and thus becomes undrawn, and then if the rockets go off-screen or collide, they also remove themselves from the arraylist and hypothetically undraw themselves.Last thing I want to mention is that the enemyship arralust becomes populated by:
if (frameCount % 100 == 0){
Random r = new Random();
int randX = r.nextInt(width - 10);
army.add(new EnemyShip(randX, -200 , 47, 47, enemyP1));
}
It takes in the parameters (x,y,w,h,Image) and is drawn off-screen first to make it seem like a side-scroller. The x variable is random, so that the ships do not all appear in the same place. frameCount is incremented by 1 every update, and the update() method is called 60 times a second.
After this huge introduction to my project, my problem is that whenever I run the game, the game just stops after a couple seconds, and eclipse gives me an error at the code:
if (arsenal.get(q).intersects(army.get(x).getR())){
I cannot figure out the problem. Any help is greatly appreciated. If more of the game code is needed, just let me know! Thanks!
EDIT:
GameScreen.update(GameScreen.java:108)
takes me to the line:
if (arsenal.get(q).intersects(army.get(x).getR())){
which is part of the loop:
for (int q = arsenal.size()-1; q>=0;q++){
if (arsenal.get(q).intersects(army.get(x).getR())){
score+=20;
army.remove(x);
arsenal.remove(q);
}
}
In the constructor is where I place the commands
arsenal.add(r);
arsenal.add(r2);
This adds them to the arraylist. In the render(Graphics2D g) method, I draw them using:
for (Rocket r :arsenal){
r.draw(g);
}
In the rocket constructor, p.currentX() is the player's current x coordinate, and p.getHalfY() and p.getHalfX() are self explanatory. They are coded so that the rocket r appears on one side, and rocket r2 is on the other side. I have also created two boolean variables, rMove and r2move. These aid in the smooth movement of the rocket, and also tell it when to move:
if (!r2move){
r2.setP(p.getR().x + 40, currentY + 12);
}
if(!rMove){
r.setP(p.currentX() - 10, currentY + 12);
}
setP(x,y) sets the rocket's position to the original place. I have talked to my teacher about this, and he said that I am sort of using both arraylists and separate objects, however I do not understand the problem. If you need any more relevant code, let me know.
This line of code also tells the rocket when to move, and that would be when the space button is fired:
if (rMove && r2move){
for (Rocket p:arsenal){
p.move();
}
}
if(r.getR().y < 0){
rMove = false;
space = false;
}
if (r2.getR().y < 0){
r2move = false;
space = false;
}
What the exception is telling you is that it has an ArrayList with 2 elements in it, which means its valid indexes are 0 and 1. But your code is calling the .get() method on this ArrayList with an index value of 2, which obviously doesn't exist and results in this exception.
Looking at the code, the problem seems to be with how you are initializing and using the loop variable q in the following lines:
for (int q = arsenal.size()-1; q>=0;q++){
if (arsenal.get(q).intersects(army.get(x).getR()))
Let's assume arsenal has 2 elements in it and hence a size of 2. As a result, q will get initialized with the value 1.
The if statement will be invoked, and arsenal.get(1) will be called. This is a valid index for the arsenal Arraylist, and so this code will pass without any error.
In the next pass of the loop, q will be incremented by 1 and will become 2. As per your loop check condition, 2 > 0, and execution will continue to the next line. The if statement will be invoked again, and arsenal.get(2) will be called. Since 2 is an invalid index for arsenal which has only 2 items, the exception you see will be thrown.
You can fix this issue by decrementing the value of q instead of incrementing it in the for loop.
I am trying to create rain animation in android/java using canvas.
The problem is the after raindrops go out of screen, they re-appear on air instead of appearing back in cloud.
What i want is,they should appear back in cloud and the distance between each row of raindrops should remain the same.
However after they go out of screen, the distance between each row changes and they stack on each other.
How can i fix that?
counter = 0;
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 10; j++) {
if(yrain[counter]<c.getHeight()){
yrain[counter] = 400+ yAdder[counter] +j*50;
yAdder[counter]+=rainSpeed;
}else{
yAdder[counter]=0;
yrain[counter] = 400+ yAdder[counter];
}
xrain[counter] = 300+ ((50) * i);
c.drawBitmap(rain[counter], xrain[counter], yrain[counter],null);
counter++;
}
}
Here is my suggestion - I have done it some years ago when needed to show some plot animation:
Double and repeat: suppose your rain starts at point Y10 and finish at Y0. Then you can generate a random matrix with drops that has to be updated in their positions moving down in relation to Y coordinate. It goes moving up to the distance (Y10-Y0)x2.
When this circle is completed, such as:
for(step=0;step<(full_cycle_steps);step++)
{ // update Y position downwards
perform_animation();
}
then you restart the animation that repeats itself.
Smooth motion: you need to apply relative DSP (digital sinal processing), so if you have Y10 to Y0, the interval is 10 integers, take this split by 100, i.e. 10x100 will give you 1000, as you are applying it with the idea of double and repeat, then you have: 10x100x2 = 2000, that means a vector with 2000 to be moving in your canvas/graph targeted coordinates, displayed in your plotting area (1000). Of course if you don't need to have the drop moving so smoothly you can make the number of your digital x analog matrix smaller.
Here you find some reference that can be insightful:
Matrix Rain
Simulating rain
jquery-matrix-digital-rain
Taking from here, you should be able to easily complete your rain animation.
I am working on a 2d arcade game where I have 5 types of circles with different sizes: The ship, the missiles, and 3 types of monsters.
This is what it looks like:
Currently I'm using brute force collision detection where I check every missile vs. every monster without taking probability of collision into account. Sadly, this makes the process REALLY slow.
This here is my Grid class, but it's incomplete. I'd greatly appreciate your help.
public class Grid {
int rows;
int cols;
double squareSize;
private ArrayList<Circle>[][] grid;
public Grid(int sceneWidth, int sceneHeight, int squareSize) {
this.squareSize = squareSize;
// Calculate how many rows and cols for the grid.
rows = (sceneHeight + squareSize) / squareSize;
cols = (sceneWidth + squareSize) / squareSize;
// Create grid
this.grid = (ArrayList[][]) new ArrayList[cols][rows]; //Generic array creation error workaround
}
The addObject method inside the Grid class.
public void addObject(Circle entity) {
// Adds entity to every cell that it's overlapping with.
double topLeftX = Math.max(0, entity.getLayoutX() / squareSize);
double topLeftY = Math.max(0, entity.getLayoutY() / squareSize);
double bottomRightX = Math.min(cols - 1, entity.getLayoutX() + entity.getRadius() - 1) / squareSize;
double bottomRightY = Math.min(rows - 1, entity.getLayoutY() + entity.getRadius() - 1) / squareSize;
for (double x = topLeftX; x < bottomRightX; x++) {
for (double y = topLeftY; y < bottomRightY; y++) {
grid[(int) x][(int) y].add(entity); //Cast types to int to prevent loosy conversion type error.
}
}
}
But that's where I am at a complete loss. I'm not even sure the source code I provided is correct. Please let me know how to make the grid based collision work. I've read basically every tutorial I could get my hands on but without much effect.
Thanks.
I found it easier (and I guess faster) to store in the object itself a binary number representing which cells the object overlaps with (instead of saving an array for every cell). I think it's called a spatial mask.
More specifically, before any collision testing, I calculate 2^(x/column_width + columns*y/row_width) for each of topLeft, topRight... then combine all those 4 in a single number (with a bitwise OR), so that I end up with a number like 5 (00000011, meaning the object hits the cells 1 and 2).
Having it this way, you then proceed to test each object with all the others, but skip the slow part if they fail to be in the same cell:
Check the bitwise AND of the numbers in both objects (this will only be !=0 if some of the cells are 1 for both objects).
If the result is something other than 0, do the proper (slow) collision checks (in your case probably pythagoras, since those are circles and I read pythagoras is faster than checking bounding squares).
Maybe I've had too much coffee, maybe I've been working too long, regardless, I'm at a loss as to what this method does, or rather, why and how it does it, could anyone shed some light upon me? What is the nextColor?
public Color nextColor() {
int max = 0, min = 1000000000, cr = 0, cg = 0, cb = 0;
for (int r = 0; r < 256; r += 4) {
for (int g = 0; g < 256; g += 4) {
for (int b = 0; b < 256; b += 4) {
if (r + g + b < 256 || r + g + b > 512) {
continue;
}
min = 1000000000;
for (Color c : colorTable) {
int dred = r - c.getRed();
int dgreen = g - c.getGreen();
int dblue = b - c.getBlue();
int dif = dred * dred + dgreen * dgreen + dblue * dblue;
if (min > dif) {
min = dif;
}
}
if (max < min) {
max = min;
cr = r;
cg = g;
cb = b;
}
}
}
}
return new Color(cr, cg, cb, 0x90);
}
UPDATE
Thanks for the responses everyone. Looking at the context of the method within the program it is clear that their intent was indeed to return a new Color that is "furthest away" from the set of existing Colors.
Thanks Sparr for posing the followup to this question, I will definitely rewrite the above with your advice in mind.
I am not very well versed in the RGB color scale. Knowing the intention of the above method is to retrieve a "complimentary?" color to the existing set of colors, will the solution provided in 1 actually be complimentary in the sense of how we perceive the color? Is there a simpler way to choose a color that will compliment the set, or does the numerical analysis of the RGB components actually yield the appropriate color?
It seems like you have colortable which is a storing a list of colors.
Then you have this strangely hardcoded colorspace of
Colors that have component which are a
multiple of 4 and are "not too bright"
but not "too dark either".
This function seems to be giving you the color in the latter which "contrasts" the best with your color table.
When I say contrast , this is defined by choosing the color that is as far as possible from the color table using the 2-norm.
Given a global array of Color objects named colorTable, this function will find the color from the following colorspace that is the closest* to each one in that array, and then the one of those colors that was farthest away:
Red, Green, Blue components a multiple of 4
Red+Green+Blue between 256 and 512
*:"closest" is defined as the lowest sum of squares of difference for each color component.
As Paul determined, this seems like a plausible, if insanely inefficiently implemented, naive approach to finding a single color that provides a high contrast with the contents of colorTable. The same result could be found with a single pass through colorTable and a bit more math, instead of some 5 million passes through colorTable, and there are much better ways to find a different color that provides a much higher average contrast.
Consider the case where the pseudo-solid defined by the points in the colorTable has a large "hollow" in its interior, such that nextColor selects the point in the center of that hollow as the nextColor. Depending on what you know about the colorTable, this case could be exceedingly rare. If it is predicted to be rare enough, and you are willing to accept a less than optimal (assuming we take nextColor's output to be optimal) solution in those cases, then a significant optimization presents itself.
In all cases except the above-described one, the color selected by nextColor will be somewhere on the surface of the minimal convex hull enclosing all of the points in the 1/64-dense colorspace defined by your loops. Generating the list of points on that surface is slightly more computationally complex than the simple loops that generate the list of all the points, but it would reduce your search space by about a factor of 25.
In the vast majority of cases, the result of that simplified search will be a point on one of the corners of that convex hull. Considering only those reduces your search space to a trivial list (24 candidates, if my mental geometry serves me well) that could simply be stored ahead of time.
If the nextColor selected from those is "too close" to your colorTable, then you could fall back on running the original type of search in hopes of finding the sort of "hollow" mentioned above. The density of that search could be adapted based on how close the first pass got, and narrowed down from there. That is, if the super fast search finds a nextColor 8 units away from its nearest neighbor in colorTable, then to do better than that you would have to find a hollow at least 16 units across within the colorTable. Run the original search with a step of 8 and store any candidates more than 4 units distant (the hollow is not likely to be aligned with your search grid), then center a radius-12 search of higher density on each of those candidates.
It occurs to me that the 1/64-dense nature (all the multiples of 4) of your search space was probably instituted by the original author for the purpose of speeding up the search in the first place. Given these improvements, you do away with that compromise.
All of this presumes that you want to stick with improvements on this naive method of finding a contrasting color. There are certainly better ways, given equal or more (which colors in colorTable are the most prevalent in your usage? what colors appear more contrast-y to the human eye?) information.
It's trying to get you another color for
a) false-color coding a data set.
b) drawing another line on the graph.