Before I show you the example code some things to mention.
Note 1:
This is often a common occurrence and in this case both the Grass and Champi class represents an obstacle for the player. To avoid a lot of duplicate coding we should create a class called Obstacle which holds all the general information about an obstacle i.e. position, size, colour etc;
Different types of obstacle e.g. Champi and Grass will inherit from Obstacle and provide any information specific to their type.
Note2:
The classes Grass and Champi should represent a single instance of this type of obstacle so neither class should be aware of other obstacles therefore do not have ArrayLists
Note3:
The World class should be aware of all obstacles in its domain so will have a single ArrayList of Obstacles. This list will hold both Grass and Champi objects.
Note4:
This is challenging because we have to test every new Obstacle against all Obstacles created so far to make sure there is no overlap. The problem is two fold
(1) The time taken to locate n obstacles is proportional to n*n. So doubling the number of Obstacles would quadruple the time and that is the best scenario.
(2) As the number of obstacles increases the chances of overlap increases and eventually it becomes impossible to find a location that would not cause overlap and the program will go into an infinite loop and appear to freeze. The sketch code below will stop creating obstacles if it takes longer than 100 milliseconds to find the next position without overlap.
In the code below I have used Mushroom and Tree for obstacles but you should be able to see how it all fits together. Anyway the sketch runs in Processing and produced the following world.
World w;
public void settings() {
size(400, 400);
}
public void setup() {
w = new World(this);
w.makeObstacles(20, 40);
w.render();
save("world.png");
}
public class World {
PApplet p;
ArrayList<Obstacle> obs = new ArrayList<Obstacle>();
public World(PApplet parent) {
p = parent;
}
public void makeObstacles(int nbrTrees, int nbrMushrooms) {
for (int i = 0; i < nbrTrees + nbrMushrooms; i++) {
PVector next = findSuitablePosition(100);
if(next == null){
break;
}
if (i < nbrTrees) {
obs.add(new Tree(p, next));
} else {
obs.add(new Mushroom(p, next));
}
}
}
private PVector findSuitablePosition(int maxTime) {
PVector pp;
boolean found;
int time = p.millis();
do {
found = true; // Assume it will not overlap
// Random position avoiding very edges of world
pp = new PVector(20 + p.random(p.width - 40), 20 + p.random(p.height - 40));
// See if it overlaps
for (Obstacle ob : obs) {
if (PVector.dist(ob.pos, pp) < 32) {
found = false; // overlap found
break;
}
}
} while (!found && p.millis() - time <= maxTime); // continue until no overlap
return found ? pp : null; // return nul if taking too long
}
public void render() {
p.pushMatrix();
p.pushStyle();
p.fill(200, 255, 200);
p.rect(0, 0, p.width, p.height);
for (Obstacle ob : obs) {
ob.render();
}
p.popStyle();
p.popMatrix();
}
}
public class Obstacle {
PApplet p;
PVector pos = new PVector();
int col;
int diam;
Obstacle(PApplet parent, PVector pos) {
p = parent;
this.pos.set(pos);
}
public void render() {
p.pushMatrix();
p.pushStyle();
p.noStroke();
p.fill(col);
println("Render " + this);
p.translate(pos.x, pos.y);
p.ellipse(0, 0, diam, diam);
p.popStyle();
p.popMatrix();
}
}
public class Tree extends Obstacle {
public Tree(PApplet p, PVector pos) {
super(p, pos);
diam = 16;
// Note you cannot use p.color(0,176, 0) bug in Processing
col = 0xFF00AA00; // darkish green
}
}
public class Mushroom extends Obstacle {
public Mushroom(PApplet p, PVector pos) {
super(p, pos);
diam = 6;
// Note you cannot use p.color(0,176, 00
col = 0xFF667722; // some other colour
}
}