all. I'm working on a small game in which a 2D array is created to house each tile of the "map" of the game. The game spawns a player and a bunch of assorted items. The cells look like this:
private String [][] cells;
...
public void set(int cellX, int cellY, String str)
{
cells[cellX][cellY] = str;
}
with each String stating what the cell in each place looks like. For instance, sometimes a wall spawns and sometimes passages are created (this is all read in from a file). So the question is this:
How can I randomly generate objects on certain cells? For instance, if I have 36 cells total (6x6), but only 23 of them can be "moved" on by the player, how can I randomly generate items that have an equal chance to spawn on each?
The code I have thus far is this.
public void draw()
{
for (int x = 0; x < cells.length; x++)
{
for (int y = 0; y < cells[w].length; y++)
{
if(cells[x][y] == "W"){
Cell wall = new Cell(config.wallImage());
wall.draw(config, x, y);
if(cells[x][y] == "P"){
Cell passage = new CellPassage(config.passageImage());
// This is where I need to check to see if the item is okay
// to be placed. If it is unable, nothing will be added to
// the Cell passage.
//passage.attemptToAdd(item);
passage.draw(config, x, y);
}
}
}
hero.draw();
}
So, had a discussion in chat with OP and this is the problem, which seemed a little confusing from the question post:
There are Item, such as Key and Gem, that may spawn in each round.
Those Item have a limit number that must be spawned every round. For example, there may spawn 5 Gem every round.
They must spawn in passages with an equal probability.
So, to solve this issue, my suggestion is:
Creating an ArrayList of Cell. Store all the cells of passages.
Generate a random number between 0 and array length - 1.
Repeat until the limit of needed items is reached.
It should look something like this:
public void addItem(Item item, int limit)
{
ArrayList<Cell> passages = new ArrayList<Cell>();
for(int x = 0; x < cells.length; x++)
{
for (int y = 0; y < cells[w].length; y++)
{
if (cells[x][y] == "P") //if it's a passage
passages.add(new Cell(x,y));
}
}
Random rand = new Random();
while(spawnedItems < limit){
if(passages.size() == 0)
throw LimitImpossibleError();
int randomNum = rand.nextInt(passages.size());
items.add(new ItemPosition(item, passages.get(randomNum))); //assuming that you have a global ArrayList of Item and respective Cell (ItemPosition) to draw them later
}
}
Another question of the OP in the discussion was how to draw the Item later. So, I gave him 2 suggestions:
Change the representation of the maze to some Object that represents what's in the maze. For example, CellInfo, which could be parent of Wall, Passage, Item, etc. This, however, would require much change on the actual work. He's actually reading the maze from a file, for example.
Having an ArrayList with Item and the respective Cell where it should be drawn. In the draw method, after drawing all the walls and passages (the only things represented in the maze with String), traverse this ArrayList and draw them in the respective position.
You could create an ArrayList of Points that are eligible to have an item on them, and then randomly select them using [your ArrayList's name].get(Math.random() * [your ArrayList's name].size())
Related
I am trying to write something that will take a X and Y coordinate and a value that represents the available movement points the selected actor has. It should then output a list of the reachable locations so that I can highlight those tiles when the actor is choosing where to move.
I managed to use a function from my pathfinding library (https://github.com/xaguzman/pathfinding) that gives me the neighbouring tiles of a tile as a List of grid cells. It can also check my Tile-map and see if the tile is walkable.
What I can't really get my head around is how I would be able to set this up so that it would run as many times as the movement points.
List<GridCell> neighbours;
NavigationTiledMapLayer navLayer;
public void getMovementPossibilities(int tileX, int tileY) {
GridCell cell1;
GridCell cell2;
cell1 = navLayer.getCell(tileX, tileY);
GridFinderOptions opt = new GridFinderOptions();
opt.allowDiagonal = true;
opt.dontCrossCorners = false;
neighbours = navLayer.getNeighbors(cell1, opt);
for (int i = 0; i < neighbours.size(); i++) {
int nX = neighbours.get(i).getX();
int nY = neighbours.get(i).getY();
cell2 = navLayer.getCell(nX, nY);
neighbours.addAll(navLayer.getNeighbors(cell2, opt));
}
}
Sounds like a case for recursion. I can't see how you're keeping track of movement points but your current method finds all tiles 1 distance away. Inside this method, you need to recall the same method, but with each of those neighbours as your new start point and with the movement points reduced by 1.
This in turn generates all the second neighbours, and then the method will be called recursively and so on...
You will want a check at the top of the method so that if the distance points left are 0, it just immediately adds the current tile to neighbours and then returns (to stop the recursive chain going on forever).
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).
I have a Processing sketch, where I am trying to just draw a scatter plot of a dataset I'm working with. I'm loading the .CSV file into an array no problem, and I'm able to format it all and throw it into a "tokens" array in order to access info in a specific row.
The problem seems to be after I parse the data, and try to map the information in order to draw the graph, all the elements in the array return 0.0.
The code is a little lengthy, but I'll just try and keep it to the relevant bits, and hopefully not leave anything out.
void setup(){
size(1024, 768)
smooth();
OfficinaBold36 = loadFont("OfficinaBold-36.vlw");
//Load data
googleData = loadStrings("RobFordCorrelate.csv");
//Init all the arrays
if((googleData == null){
println("Error, one of the datasets could not be loaded.");
}
else{
totalSearch = googleData.length-1;
googleDates = new int[totalSearch];
relativeInterest = new float[totalSearch];
//Also grabbing all column names for use later, if needed
columnNames = googleData[0].split(",");
}
parseGoogleData();
}
This the parseGoogleData function:
void parseGoogleData(){
/*Grab all the dates, we have to loop backwards through this set,
because the CSV was formatted in reverse chronological order.
Note that because of the row > 0 condition, we will not include
row 0, i.e the column title row */
for (int row = googleData.length-1 ; row > 0; row--){
/*counter going up, we need a reference to a counter that
counts up, while the main index counts down, to be
able to assign certain values*/
int i = 0;
//Grab all the elements in that row, splitting them at the comma
googleTokens = googleData[row].split(",");
// Grab the elements we want to look at, the date, index 0
googleDates[i] = int(googleTokens[0]);
//and relative interest in the search term "rob ford", index 1
relativeInterest[i] = float(googleTokens[1]);
//increment our 2nd counter
i++;
}
}
Also the relativeInterest array is a global variable, so I have access to it in both setup and draw. Now if I println(relativeInterest[i]) in that last for loop, it will return all the proper data. However, if I print it in the draw() loop below, they all return zero (I'm only including the lines that refer to drawing each point, as I have a lot of positioning data for the x and y-axis that I didn't want to include):
void draw(){
for(int row = 0 ; row < totalSearch; row++){
float y = map(relativeInterest[row], -3, 3, 0, width);
float x = row*lb;
int d = 5;
noStroke();
fill(#FFBA00, 180);
ellipse(x, y, d, d);
}
When I run this code, each ellipse is in the exact same y position, I have no idea when the data in the array is getting set to 0? The stuff in the draw loop isn't accessing the array until these lines hit, but I don't know why they're being converted to 0.0?
Any/all help is greatly appreciated. Sorry for such a long post!
Move your int i = 0; outside of your for loop. Also you want Integer.valueOf and Float.valueOf, not int() and float() respectively.
In the game i'm building, I have made a basic collision detection system.
My current method is explained below:
I workout where the player will be in the next step of the game:
double checkforx = x+vx;
double checkfory = y+vy;
I then check for a collision with blocks (1) in mapArray.
public static Boolean checkForMapCollisions(double character_x,double character_y){
//First find our position in the map so we can check for things...
int map_x = (int) Math.round((character_x-10)/20);
int map_y = (int) Math.round((character_y-10)/20);
//Now find out where our bottom corner is on the map
int map_ex = (int) Math.round((character_x+10)/20);
int map_ey = (int) Math.round((character_y+10)/20);
//Now check if there's anything in the way of our character being there...
try{
for(int y = map_y; y <= map_ey; y++){
for(int x = map_x; x <= map_ex; x++){
if (levelArray[y][x] == 1){
return true;
}
}
}
}catch (Exception e){
System.out.println("Player outside the map");
}
return false;
}
If true is returned {nothing}
If false is returned {Player physics}
I need the player to be able to land on a block and then be able to walk around but I cannot find and adequate tutorial for this.
Can someone give me an idea on how to run my collision detection and/or movement?
There are 2 parts to this question. Collision detection, meaning determining whether a volume is touching or intersecting another volume. The second is collision response. Collision response is the physics portion.
I'll cover collision detection here as that's primarily what you asked about.
Ddefine a class for the map like so:
int emptyTile = 0;
//this assumes level is not a ragged array.
public boolean inBounds(int x, int y){
return x>-1 && y>-1 && x<levelArray[0].length && y<levelArray.length;
}
public boolean checkForCollisions(Rectangle rectangle){
boolean wasCollision = false;
for(int x=0;x<rectangle.width && !wasCollision;x++){
int x2 = x+rectangle.x;
for(int y=0;y<rectangle.height && !wasCollision;y++){
int y2 = y+rectangle.y;
if(inBounds(x2,y2) && levelArray[y2][x2] != emptyTile){
//collision, notify listeners.
wasCollision=true;
}
}
}
}
Do not make your methods static. You probably want more than one instance of a level right? Static is for when you need to share state which remains constant across multiple instances of a class. Level data will surely not remain constant for every level.
Instead of passing in a coordinate, try passing in an entire rectangle. This rectangle will be the bounding box of your character (the bounding box is also sometimes referred to as AABB, which means Axis-aligned bounding box, just FYI in case you're reading tutorials online for this sort of thing.) Let your Sprite class decide what its bounding rectangle is, that's not the map class's responsibility. All the map should be used for is maybe rendering, and whether a rectangle is overlapping tiles which are not empty.
I am sorry for a very shitty explanation but here is my github code and it will help better.
https://github.com/Quillion/Engine
Just to explain what I do. I have character object (https://github.com/Quillion/Engine/blob/master/QMControls.java) and it has vectors and a boolean called standing. Every time boolean standing is false. Then we pass it to the engine to check for collision, if collision happens then standing is true and y vector is 0. As to x vector whenever you press any arrow keys you make the xvector of the object to whatever value you want. And in the update loop you displace the given box by the amount of speed.
I am working on a platformer game that will use tile maps, which I don't know if is a good idea!
I've made a neat tile map editor with tools for setting a spawn point etc. but now that I want to be able to test play the game after editing map and for future use I need of course integrate physics which I've done with Box2D which comes with LibGDX!
I am creating a method to create a collision map from tile map which has data if tile is collideable or not!
So I came up with this great idea:
loop through the map and if we find a colliding tile loop through its neighbor tiles and see if they're colliding too, and do this til noncolliding tile is found when we set width and height for the colliding rectangle
after we got bunch of rectangle I sort them in order from biggest square to smallest so we get biggest pieces and I add the rectangles to final list and check against the final rect if any of them overlaps with current body so I don't have overlapping bodys
But you know, code tells more than 1000 words, right?
public void createBody() {
List<Rectangle> allRects = new ArrayList<Rectangle>();
for(int x = 0; x < info.getWidth(); x++) {
for(int y = 0; y < info.getHeight(); y++) {
if(tiles[x][y].getInfo().isColliding()) {
int width = 1;
int height = 1;
//loop through neighbors horizontally
for(int i = 0; i < info.getWidth() - x; i++) {
if(!tiles[x + i][y].getInfo().isColliding()) {
//if tile is not clipped, we set width to i which is current x offset
width = i;
break;
}
}
//only if width is bigger than zero can the rect have any tiels..
if(width > 0) {
boolean breakingBad = false;
//loop through neighbors horizontally
for(int j = 0; j < info.getHeight() - y; j++) {
//loop though neigbors vertizally
for(int i = 0; i < width; i++) {
//check if tile is not colliding
if(!tiles[x + i][y + j].getInfo().isColliding()) {
//and if so, we set height to j which is current y offset
height = j;
//breaking bad aka leaving both loops
breakingBad = true;
break;
}
}
if(breakingBad) {
break;
}
}
}
if(width * height > 0)
allRects.add(new Rectangle(x, y, width, height));
}
}
}
Collections.sort(allRects, new Comparator<Rectangle>() {
#Override
public int compare(Rectangle o1, Rectangle o2) {
Integer o1Square = o1.width * o1.height;
Integer o2Square = o2.width * o2.height;
return o2Square.compareTo(o1Square);
}
});
List<Rectangle> finalRects = new ArrayList<Rectangle>();
mainloop:
for(Rectangle rect: allRects) {
for(Rectangle finalRect: finalRects) {
if(finalRect.contains(rect)) {
continue mainloop;
}
}
finalRects.add(rect);
}
for(Rectangle rect: finalRects) {
PolygonShape polyShape = new PolygonShape();
polyShape.setAsBox((float)rect.getWidth() / 2, (float)rect.getHeight() / 2, Vector2.tmp.set((float)rect.getCenterX(), (float)rect.getCenterY()), 0f);
mapBody.createFixture(polyShape, 1);
polyShape.dispose();
}
}
however this sill seems pretty inefficient because for some reasons its still creating smaller fixtures than it could be possible, for example in upper right corner
also its creating single fixtures in the corners of the center rectangle and I can't figure out why!
Is the whole idea all inefficient, and should I use other method or manually create collision maps or what could be the best idea?
Originally each tile was its own fixture which caused weird bugs on their edges as expected
First off, a custom tile mapping tool is a great idea on the surface, but you're reinventing the wheel.
libGDX has built-in support for TMX maps.
http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/maps/tiled/TmxMapLoader.html
Instead of using your homebrew editor, you can use a full featured editor such as this Tiled - http://www.mapeditor.org/
So once you have a better system in place for your maps, I would look at this from an object oriented perspective. Since you want to use box2d physics, each collidableTile HAS A body. So all you need to do is assign a physics body to each collidableTile, and set the size according to your standard tile size.
Don't forget that there is a difference between the box2d world and your game screen, where box2d is measured in metric units, and your screen is measured in pixels. So you need to do some math to set positions and size properly. If you want a set of tiles to share a body, you may want to pass in the body as a parameter when you construct each collidableTile, and then adjust the size of the body based on how many adjacent tiles you can find. More complex shapes for the physics body may be more complex.
You can also save resources by setting those tiles to 'sleep', where box2d does a reduced simulation on those bodies until it detects a collision. If you're only using box2d for collision detection on terrain, you may want to consider other options, like using shape libraries to detect intersections, and then setting the box2d physics on your player characters body to stop downward acceleration while there is contact, or something.