How do I determine a movable area in game squares - javascript

I'm building a turn based HTML game based on a 2D square grid. Each grid square could take a variable number of movement points to cross (IE: 1 MP for roads, 1.5 MP for grasslands, 2 MP for forests, etc). When the user clicks on a unit I want to determine all possible movable spaces with said unit's allotted movement points so that I can highlight them and make them clickable.
Is there a free library available to do this? I've seen a few pathing algorithms but nothing about determining movable area. How do other game developers handle this problem? I'm open to both vanilla JS and JQuery solutions.

Well, I decided to try and attack this myself. I've never been great at these sorts of algorithms so I'm sure there's a more efficient way to handle it than what I've done. However, for my purposes it runs quickly enough and is very simple and easy to understand.
In case it's helpful to anyone else looking to do the same, I've included the code below. This is an updated version of my original answer, which I modified to also store the path taken so that you can show the units moving through the correct spaces. This answer uses JQuery in the lower examples, but only in a few places; you can easily enough replace them with vanilla JS. And the first block of code, containing the actual path/area finding functionality, is pure JS.
<script>
var possibleMovementAreaArray = new Array(); // This array will hold our allowable movement tiles. Your other functions can access this after running possibleMovementArea().
function possibleMovementArea(unitIndex) {
// I'm storing each unit in my game in an array. So I pass in the index of the unit I want to determine the movement area for.
var x = unitList[unitIndex][10]; // x coordinate on the playgrid
var y = unitList[unitIndex][11]; // y coordinate on the playgrid
var mp = unitList[unitIndex][15]; // number of movement points
possibleMovementAreaArray.length = 0; // Clear our array so previous runs don't interfere.
findPossibleMovement(x, y, mp);
}
function findPossibleMovement(x, y, mp, prevStepX, prevStepY) {
// This is a recursive function; something I'm not normally too good at.
for (var d=1; d<=4; d++) {
// We run through each of the four cardinal directions. Bump this to 8 and add 4 more cases to include corners.
if (d == 1) {
// Check Up
var newX = x;
var newY = y - 1;
} else if (d == 2) {
// Check Down
var newX = x;
var newY = y + 1;
} else if (d == 3) {
// Check Left
var newX = x - 1;
var newY = y;
} else if (d == 4) {
// Check Right
var newX = x + 1;
var newY = y;
}
// Check to see if this square is occupied by another unit. Two units cannot occupy the same space.
spaceOccupied = false;
for (var j=1; j<=numUnits; j++) {
if (unitList[j][10] == newX && unitList[j][11] == newY)
spaceOccupied = true;
}
if (!spaceOccupied) {
// Modify this for loop as needed for your usage. I have a 2D array called mainMap that holds the ID of a type of terrain for each tile.
// I then have an array called terList that holds all the details for each type of terrain, such as movement points needed to get past.
// This for loop is just looking up the ID of the terrain for use later. Sort of like a "SELECT * FROM terrainInfo WHERE ID=terrainOfCurrentTile".
for (var j=1; j<=numTerrains; j++) {
if (newX > 0 && newX <= mapWidth && newY > 0 && newY <= mapHeight && terList[j][1] == mainMap[newX][newY])
break; // After finding the index of terList break out of the loop so j represents the correct index.
}
if (j <= numTerrains) { // Run if an actual terrain is found. No terrain is found if the search runs off the sides of the map.
var newMp = mp - terList[j][7]; // Decrement the movement points for this particular path.
if (newMp >= 0) { // Only continue if there were enough movement points to move to this square.
// Check to see if this square is already logged. For both efficiency and simplicity we only want each square logged once.
var newIndex = possibleMovementAreaArray.length
var alreadyLogged = false
if (possibleMovementAreaArray.length > 0) {
for (var j=0; j<possibleMovementAreaArray.length; j++) {
if (possibleMovementAreaArray[j][1] == newX && possibleMovementAreaArray[j][2] == newY) {
alreadyLogged = true;
var alreadyLoggedIndex = j;
}
}
}
if (!alreadyLogged) {
// This adds a row to the array and records the x and y coordinates of this tile as movable
possibleMovementAreaArray[newIndex] = new Array(6);
possibleMovementAreaArray[newIndex][1] = newX;
possibleMovementAreaArray[newIndex][2] = newY;
possibleMovementAreaArray[newIndex][3] = prevStepX; // This tracks the x coords of the steps taken so far to get here.
possibleMovementAreaArray[newIndex][4] = prevStepY; // This tracks the y coords of the steps taken so far to get here.
possibleMovementAreaArray[newIndex][5] = newMp; // Records remaining MP after the previous steps have been taken.
}
if (alreadyLogged && newMp > possibleMovementAreaArray[alreadyLoggedIndex][5]) {
// If this tile was already logged, but there was less MP remaining on that attempt, then this one is more efficient. Update the old path with this one.
possibleMovementAreaArray[alreadyLoggedIndex][3] = prevStepX;
possibleMovementAreaArray[alreadyLoggedIndex][4] = prevStepY;
possibleMovementAreaArray[alreadyLoggedIndex][5] = newMp;
}
if (newMp > 0) {
// Now update the list of previous steps to include this tile. This list will be passed along to the next call of this function, thus building a path.
if (prevStepX == '') {
var newPrevStepX = [newX];
var newPrevStepY = [newY];
} else {
// This code is required to make a full copy of the array holding the existing list of steps. If you use a simple equals then you just create a reference and
// subsequent calls are all updating the same array which creates a chaotic mess. This way we store a separate array for each possible path.
var newPrevStepX = prevStepX.slice();
newPrevStepX.push(newX);
var newPrevStepY = prevStepY.slice();
newPrevStepY.push(newY);
}
// If there are still movement points remaining, check and see where we could move next.
findPossibleMovement(newX, newY, newMp, newPrevStepX, newPrevStepY);
}
}
}
}
}
}
</script>
After running the above, you can then loop through the array to find all usable tiles. Here is how I did it:
<script>
// Shows the movement area based on the currently selected unit.
function showMovement() {
var newHTML = "";
curAction = "move";
possibleMovementArea(curUnit); // See above code
for (x=0; x<possibleMovementAreaArray.length; x++) {
// Loop over the array and do something with each tile. In this case I'm creating an overlay that I'll fade in and out.
var tileLeft = (possibleMovementAreaArray[x][1] - 1) * mapTileSize; // Figure out where to absolutely position this tile.
var tileTop = (possibleMovementAreaArray[x][2] - 1) * mapTileSize; // Figure out where to absolutely position this tile.
newHTML = newHTML + "<img id='path_" + possibleMovementAreaArray[x][1] + "_" + possibleMovementAreaArray[x][2] + "' onClick='mapClk(" + possibleMovementAreaArray[x][1] + ", " + possibleMovementAreaArray[x][2] + ", 0);' src='imgs/path.png' class='mapTile' style='left:" + tileLeft + "px; top:" + tileTop + "px;'>";
}
$("#movementDiv").html(newHTML); // Add all those images into a preexisting div.
$("#movementDiv").css("opacity", "0.5"); // Fade the div to 50%
$("#movementDiv").show(); // Make the div visible.
startFading(); // Run a routine to fade the div in and out.
}
</script>
Since we determined the path, we can easily show movement as well by looping through the stored information:
<script>
for (j=0; j<possibleMovementAreaArray[areaIndex][3].length; j++) {
// This loop moves the unit img to each tile on its way to its destination. The final destination tile is not included.
var animSpeed = 150; // Time in ms that it takes to move each square.
var animEase = "linear"; // We want movement to remain a constant speed through each square in this case.
var targetLeft = (possibleMovementAreaArray[areaIndex][3][j]-1) * mapTileSize; // This looks at each step in the path array and multiplies it by tile size to determine the new horizonal position.
var targetTop = (possibleMovementAreaArray[areaIndex][4][j]-1) * mapTileSize; // This looks at each step in the path array and multiplies it by tile size to determine the new vertical position.
$("#char_"+curUnit).animate({"left":targetLeft, "top":targetTop}, animSpeed, animEase); // Do the animation. Subsequent animations get queued.
}
// Now we need to move to that last tile.
newLeft = (x-1) * mapTileSize;
newTop = (y-1) * mapTileSize;
$("#char_"+curUnit).animate({"left":newLeft, "top":newTop}, 400, "easeOutCubic"); // Slow unit at the end of journey for aesthetic purposes.
$("#char_"+curUnit).addClass("unitMoved", 250); // Turns the image grayscale so it can easily be seen that it has already moved.
</script>
Hopefully this is helpful to others.

Related

Javascript matrix check neighbor state to find edges

i am making a virtual Hama beads tool online. (its a kids toy where you put plastic beads on a matrix and iron them to make them stick together and form a solid shape)like this one below (real hama)
So far i managed to make this work (you can try it out there : https://static.club1.fr/alixturcq/hama%20dev/ ) but i would like to make it more elaborate and have the beads on the edges more realistic with round corners where there are no neighbors (virtual hama)
This would mean having a special image for each of theses neighbor cases like this (neighbor_cases)
So each bead is an object, and i have added a "neighbor" parameter to display the correct image.
Every object is automatically placed on the matrix at start but "isOn" is false, so nothing is displayed. When I click on the matrix, I change the 'isOn' boolean to true and the hama bead displays.
The objects are stored in a 1D array called hamas[], but are being displayed as a 2D array, so i can manipulate it as 2D.
class hama {
constructor(isOn, posx, posy, color, neighbor) {
this.posx = posx;
this.posy = posy;
this.color = color;
this.isOn = isOn;
this.neighbor = neighbor;
this.variation = int(random(0,6));
map
}
display() {
if (this.isOn == true) {
if (ironed == true) {
image(hamas_img[this.color][this.neighbor], this.posx, this.posy, CELL_SIZE, CELL_SIZE);
}
else {
image(ironed_hamas_img[this.color][this.neighbor], this.posx, this.posy, CELL_SIZE, CELL_SIZE);
}
}
}
}
Every time I place a bead i need the program to check every objects if they are On, and also what is the 'isOn' state of each of the neighbors in the matrix.
So far I've done this in pseudo-code but I hope there are smarter ways.
// creating an array of neighbor indexes (clockwise around the object)
checkAround[(+1); (width+1); (width); (width-1); (-1); (-width-1); (-width); (-width+1)]
// creating an empty string that is going to be the neighbor code
neighborCode= "";
// check around object to get neighborstate
for (int i=0; i<checkAround.count; i++){
if (hammas[hammaIndex+checkAround[i]].isOn==true){
neighborCode += "O"
}
else {
neighborCode += "X"
}
}
Then I get neighbourCode strings looking like "XOOXOXOO" and map all these code to appropriate neighbour state number and display the correct image.
So this is rather twisted, I was hoping someone could help me find a smarter solution !
And also I have to find a way to avoid checking non existing indexes on the edges of the matrix, but this should not be to hard to find.
Thanks in advance
Al
What you are looking for is edge detection on a pixel array and has been solved many times before. You can solve it yourself and it's fun to figure out yourself.
When a problem exists in 2D space, it often helps to think in 2D.
But your data is structured in 1D (which is fine, and the convention)
Each pixel/bead has a position in the 1D array -let's call that i- and a position in 2D space, defined by x and y.
The relationship between those is like this: i = x + y * w
( Where w is the width of the image)
With this information you can traverse the 1D array through x and y quite easily and decide for each pixel what its neighbours are doing.
example:
for(var x = 0; x < width; x++){
for(var y = 0; y < height; y++){
var i = x + y * width; // id of the current pixel being checked.
// Calculate neighbour ids:
var iNorth = x + (y - 1) * width;
var iSouth = x + (y + 1) * width;
var iEast = x + 1 + y * width;
var iWest = x - 1 + y * width;
var iNorthWest = (x - 1) + (y - 1) * width; // haha Kanye
// etc.. you get the point
// Now encode all those vars in one single byte
// Use that byte to choose the right image for the bead at position I
// Don't forget to deal with your edge cases
}
}
You can optimize later. (eg. You don't have to update the whole image each time you change one pixel, only its neighbors are affected.)
Your "OXOOXOOO" string is a perfectly acceptable way of storing the different states of a bead. A more nerdy approach would be to just store bits in a 8 bit byte.(because each pixel has only 8 neighbours..)
Those bytes can be stored in a buffer with the same exact structure as your actual pixel data.
I think this should get you started without spoiling too much of the fun?

How do you avoid the "RangeError: Maximum call stack size exceeded" error?

I'm currently working on a maze generating algorithm called recursive division. The algorithm is quite simple to understand: Step 1: if the height of your chamber is smaller than the width, divide your grid/chamber with a vertical line. If the height is greater than the width, then divide your chamber with a horizontal line. Step 2: Repeat step 1 with the sub-chambers that were created by the lines. You want to repeat these steps until you get a maze (until the width or height equals 1 unit).
The problem that I have with this algorithm is that JavaScript prints out a RangeError, meaning that I called the function that creates the maze too many times (I'm trying to implement this algorithm with a recursive function). Is there any way to avoid/prevent this from happening? Or am I missing something important in my code that makes the algorithm not work properly?
I have tried to implement a trampoline function, but since I'm a beginner I just don't understand it well enough to implement my self. I have also restarted my entire project ruffly 3 times with some hope that I will come up with a different approach to this problem, but I get the same error every time.
My code here:
//leftCord = the left most x coordinate of my chamber/grid, upCord = the upmost y coordinate of my
grid etc.
//(0, 0) IS POSITIONED IN THE LEFT TOP NODE OF MY GRID
function createMaze(leftCord, rightCord, upCord, downCord) {
var height = Math.abs(downCord - upCord);
var width = Math.abs(rightCord - leftCord);
if (height < 2 || width < 2) {
//The maze is completed!
return;
} else {
if (height < width) {
//cut the chamber/grid vertically
//Getting a random number that's EVEN and drawing the function x = 'random number' on the grid
var x = randomNum(leftCord / 2, rightCord / 2) * 2;
var lineX = [];
for (i = upCord; i < downCord; i++) {
lineX.push(grid[i][x]);
}
//Making a random door/passage and making sure it's ODD
var randomDoor = randomNum(0, lineX.length / 2) * 2 + 1;
lineX.splice(randomDoor, 1);
//Drawing the line
for (i = 0; i < lineX.length; i++) {
lineX[i].className = "wall";
}
//Making the same thing again, but with the left and right sub-chambers that were created by the line
createMaze(leftCord, x, upCord, downCord);
createMaze(x, rightCord, upCord, downCord);
} else {
//cut the chamber/grid horizontally
//Getting a random number that's EVEN and drawing the function y = 'random number' on the grid
var y = randomNum(0, downCord / 2) * 2;
var lineY = [];
for (i = leftCord; i < rightCord; i++) {
lineY.push(grid[y][i]);
}
//Making a random door/passage and making sure it's ODD
var randomDoor = randomNum(0, lineY.length / 2) * 2 + 1;
lineY.splice(randomDoor, 1);
//Drawing the line
for(i = 0; i < lineY.length; i++){
lineY[i].className = "wall";
}
//Making the same thing again, but with the upper and lower-chambers that were created by the line
createMaze(leftCord, rightCord, upCord, y);
createMaze(leftCord, rightCord, y, downCord);
}
}
}
This is happening because you never initialize i with var- it is sent into the global scope and is overwritten each function call.

AABB collision resolution slipping sides

So, I am currently reinventing the wheel (and learning a lot) by trying my hand at making a simple physics engine for my game engine. I have been searching the internet, trying (and failing) to fix my current problem. There are a lot of resources out there on the subject, but none of those that I have found seem to apply to my case.
THE PROBLEM IN SHORT: The collision resolution does not work as intended on some of the corners when two rectangles are colliding. How it fails varies based on the dimensions of the rectangles. What I am looking for is a "shortest overlap" kind of resolution for the collision or another fairly simple solution (I am open for suggestions!). (Scroll down for a better explaination and illustrations).
WARNING: The following code is probably not very efficient...
First of all, here is my physics loop. It simply loops through all of the game entities and checks if they collide with any other game entities. It is not efficient (n^2 and all of that), but it works for now.
updatePhysics: function(step) {
// Loop through entities and update positions based on velocities
for (var entityID in Vroom.entityList) {
var entity = Vroom.entityList[entityID];
if (entity.physicsEnabled) {
switch (entity.entityType) {
case VroomEntity.KINEMATIC:
entity.pos.x += entity.vel.x * step;
entity.pos.y += entity.vel.y * step;
break;
case VroomEntity.DYNAMIC:
// Dynamic stuff
break;
}
}
}
// Loop through entities and detect collisions. Resolve collisions as they are detected.
for (var entityID in Vroom.entityList) {
var entity = Vroom.entityList[entityID];
if (entity.physicsEnabled && entity.entityType !== VroomEntity.STATIC) {
for (var targetID in Vroom.entityList) {
if (targetID !== entityID) {
var target = Vroom.entityList[targetID];
if (target.physicsEnabled) {
// Check if current entity and target is colliding
if (Vroom.collideEntity(entity, target)) {
switch (entity.collisionType) {
case VroomEntity.DISPLACE:
Vroom.resolveTestTest(entity, target);
break;
}
}
}
}
}
}
}
},
Here is the code for the actual collision detection. This also seems to work alright.
collideEntity: function(entity, target) {
if (entity.getBottom() < target.getTop() || entity.getTop() > target.getBottom() ||  entity.getRight() < target.getLeft() ||  entity.getLeft() > target.getRight()) {
return false;
}
return true;
},
Here is where the problems start to pop up. I want the entity to simply be "pushed" out of the target entity and have the velocity set to 0. This works fine as long as both the entity and the target are perfect squares. If let's say the entity (the player figure in the gif) is a rectangle, then the collision will "slipp" when colliding the longest sides (the X axis) with the target (the square). If I swap the player dimensions so that it is short and wide, then the same problem appears for the Y axis instead.
resolveTestTest: function(entity, target) {
var normalizedX = (target.getMidX() - entity.getMidX());
var normalizedY = (target.getMidY() - entity.getMidY());
var absoluteNormalizedX = Math.abs(normalizedX);
var absoluteNormalizedY = Math.abs(normalizedY);
console.log(absoluteNormalizedX, absoluteNormalizedY);
// The collision is comming from the left or right
if (absoluteNormalizedX > absoluteNormalizedY) {
if (normalizedX < 0) {
entity.pos.x = target.getRight();
} else {
entity.pos.x = target.getLeft() - entity.dim.width;
}
// Set velocity to 0
entity.vel.x = 0;
// The collision is comming from the top or bottom
} else {
if (normalizedY < 0) {
entity.pos.y = target.getBottom();
} else {
entity.pos.y = target.getTop() - entity.dim.height;
}
// Set velocity to 0
entity.vel.y = 0;
}
},
Collision on the Y axis works with these shapes
Collision on the X axis slips with these shapes
What can I do to fix this slipping problem? I have been bashing my head against this for the last 5 days, so I would be immensely grateful if some one could help push me in the right direction!
Thank you :)
-- EDIT: --
The slipping also happens if only moving in one direction along the left or right side.
-- EDIT 2 WORKING CODE: --
See my answer below for an example of the working code!
The important logical error you have made is this line:
if (absoluteNormalizedX > absoluteNormalizedY) {
This only works if both entities are square.
Consider a near-extremal case for your X-slipping example: if they almost touch at the corner:
Although the diagram is a little exaggerated, you can see that absoluteNormalizedX < absoluteNormalizedY in this case - your implementation would move on to resolve a vertical collision instead of the expected horizontal one.
Another error is that you always set the corresponding velocity component to zero regardless of which side the collision is on: you must only zero the component if is it in the opposite direction to the collision normal, or you won't be able to move away from the surface.
A good way to overcome this is to also record the collided face(s) when you do collision detection:
collideEntity: function(entity, target) {
// adjust this parameter to your liking
var eps = 1e-3;
// no collision
var coll_X = entity.getRight() > target.getLeft() && entity.getLeft() < target.getRight();
var coll_Y = entity.getBottom() > target.getTop() && entity.getTop() < target.getBottom();
if (!(coll_X && coll_Y)) return 0;
// calculate bias flag in each direction
var bias_X = entity.targetX() < target.getMidX();
var bias_Y = entity.targetY() < target.getMidY();
// calculate penetration depths in each direction
var pen_X = bias_X ? (entity.getRight() - target.getLeft())
: (target.getRight() - entity.getLeft());
var pen_Y = bias_Y ? (entity.getBottom() - target.getUp())
: (target.getBottom() - entity.getUp());
var diff = pen_X - pen_Y;
// X penetration greater
if (diff > eps)
return (1 << (bias_Y ? 0 : 1));
// Y pentration greater
else if (diff < -eps)
return (1 << (bias_X ? 2 : 3));
// both penetrations are approximately equal -> treat as corner collision
else
return (1 << (bias_Y ? 0 : 1)) | (1 << (bias_X ? 2 : 3));
},
updatePhysics: function(step) {
// ...
// pass collision flag to resolver function
var result = Vroom.collideEntity(entity, target);
if (result > 0) {
switch (entity.collisionType) {
case VroomEntity.DISPLACE:
Vroom.resolveTestTest(entity, target, result);
break;
}
}
// ...
}
Using a bit flag instead of a boolean array for efficiency. The resolver function can then be re-written as:
resolveTestTest: function(entity, target, flags) {
if (!!(flags & (1 << 0))) { // collision with upper surface
entity.pos.y = target.getTop() - entity.dim.height;
if (entity.vel.y > 0) // travelling downwards
entity.vel.y = 0;
}
else
if (!!(flags & (1 << 1))) { // collision with lower surface
entity.pos.y = target.getBottom();
if (entity.vel.y < 0) // travelling upwards
entity.vel.y = 0;
}
if (!!(flags & (1 << 2))) { // collision with left surface
entity.pos.x = target.getLeft() - entity.dim.width;
if (entity.vel.x > 0) // travelling rightwards
entity.vel.x = 0;
}
else
if (!!(flags & (1 << 3))) { // collision with right surface
entity.pos.x = target.getRight();
if (entity.vel.x < 0) // travelling leftwards
entity.vel.x = 0;
}
},
Note that unlike your original code, the above also allows corners to collide - i.e. for velocities and positions to be resolved along both axes.
MY WORKING CODE
So with some help and guidance from the amazing #meowgoesthedog I finally got on the right track and found what I was looking for. The problem (as #meowgoesthedog pointed out) was that my code was really only going to work with squares. The solution was to check the intersection of the colliding bodies and solve based on the shortest intersection. Note: this will probably not be a suitable solution if you need accurate physics with small and fast moving objects. The code for finding the intersection depth is based on this: https://github.com/kg/PlatformerStarterKit/blob/0e2fafb8dbc845279fe4116c37b6f2cdd3e636d6/RectangleExtensions.cs which is related to this project: https://msdn.microsoft.com/en-us/library/dd254916(v=xnagamestudio.31).aspx.
Here is my working code:
My physics loop has not been changed much, except for some better names for some functions.
updatePhysics: function(step) {
// Loop through entities and update positions based on velocities
for (var entityID in Vroom.entityList) {
var entity = Vroom.entityList[entityID];
if (entity.physicsEnabled) {
switch (entity.entityType) {
case VroomEntity.KINEMATIC:
entity.pos.x += entity.vel.x * step;
entity.pos.y += entity.vel.y * step;
break;
case VroomEntity.DYNAMIC:
// Dynamic stuff
break;
}
}
}
// Loop through entities and detect collisions. Resolve collisions as they are detected.
for (var entityID in Vroom.entityList) {
var entity = Vroom.entityList[entityID];
if (entity.physicsEnabled && entity.entityType !== VroomEntity.STATIC) {
for (var targetID in Vroom.entityList) {
if (targetID !== entityID) {
var target = Vroom.entityList[targetID];
if (target.physicsEnabled) {
// Check if current entity and target is colliding
if (Vroom.collideEntity(entity, target)) {
switch (entity.collisionType) {
case VroomEntity.DISPLACE:
Vroom.resolveDisplace(entity, target);
break;
}
}
}
}
}
}
}
},
The collision detection remains the same as well.
collideEntity: function(entity, target) {
if (entity.getBottom() < target.getTop() || entity.getTop() > target.getBottom() ||  entity.getRight() < target.getLeft() ||  entity.getLeft() > target.getRight()) {
return false;
}
return true;
},
Here is the code that basically fixes the problem. The comments in the code should explain what it does pretty well.
getIntersectionDepth: function(entity, target) {
// Calculate current and minimum-non-intersecting distances between centers.
var distanceX = entity.getMidX() - target.getMidX();
var distanceY = entity.getMidY() - target.getMidY();
var minDistanceX = entity.halfDim.width + target.halfDim.width;
var minDistanceY = entity.halfDim.height + target.halfDim.height;
// If we are not intersecting at all, return 0.
if (Math.abs(distanceX) >= minDistanceX || Math.abs(distanceY) >= minDistanceY) {
return {
x: 0,
y: 0,
};
}
// Calculate and return intersection depths.
var depthX = distanceX > 0 ? minDistanceX - distanceX : -minDistanceX - distanceX;
var depthY = distanceY > 0 ? minDistanceY - distanceY : -minDistanceY - distanceY;
return {
x: depthX,
y: depthY,
};
},
Here is the updated resolving function. It now takes intersection depth in to account when determining axis of collision and then uses the sign of the intersection depth for the colliding axis when determining the direction to resolve.
resolveDisplace: function(entity, target) {
var intersection = Vroom.getIntersectionDepth(entity, target);
if (intersection.x !== 0 && intersection.y !== 0) {
if (Math.abs(intersection.x) < Math.abs(intersection.y)) {
// Collision on the X axis
if (Math.sign(intersection.x) < 0) {
// Collision on entity right
entity.pos.x = target.getLeft() - entity.dim.width;
} else {
// Collision on entity left
entity.pos.x = target.getRight();
}
entity.vel.x = 0;
} else if (Math.abs(intersection.x) > Math.abs(intersection.y)) {
// Collision on the Y axis
if (Math.sign(intersection.y) < 0) {
// Collision on entity bottom
entity.pos.y = target.getTop() - entity.dim.height;
} else {
// Collision on entity top
entity.pos.y = target.getBottom();
}
entity.vel.y = 0;
}
}
},
Thank you all for your help!
The problem may be that you're correcting both X and Y collision based on the same position:
Player is at a certain position. Let's check collision.
Player's bottom right corner overlaps top left corner of object.
X position is corrected: Player is moved to the left.
Player's bottom right corner overlaps top left corner of object.
Y position is corrected: Player is moved up.
End result: The player is moved up and to the left.
You probably need to "get" the player's position again, between checks.

javascript game sprite positioning

I'm trying to create a chess board, and place it in the middle of the screen, so far i cannot get it to be directly in the center. i don't want to hard code the position to the screen because i'm going to be dealing with different screen sizes.
var winsize = cc.director.getWinSize();
var centerpos = cc.p(winsize.width / 2, winsize.height / 2);
for (i=0; i<64; i++){
var tile = cc.Sprite.create(res.tile_png);
this.addChild(tile,0);
tile.setPosition(winsize.width+i%8*50/-10, winsize.height-Math.floor(i/8)*50);
}
But the tiles and positioning is completely off
#jumpman8947, if you're using Cocos2d js perhaps you have a similar line: cc.view.setDesignResolutionSize(480, 320, cc.ResolutionPolicy.SHOW_ALL);
In this particular case the game will scale to any sceeen, but still run in 480x320 resolution, so no matter what screen resoultion you use, the center in the cocos world would always be cc.p(240, 160) so no matter what's the window size or the screen resolution, the resolution of the game stays the same
You can read more about resolution policies here (and in official js-doc):
http://www.cocos2d-x.org/wiki/Multiple_Resolution_Policy_for_Cocos2d-JS
Also please be aware, that the Sprite position in Cocos is the position of the centre of the sprite, not bottom left corner
In your question it's not completely clear exactly what you want. However, I made some assumptions. The explanation for my solution is embedded in the comments in the code below.
// var winsize = cc.director.getWinSize();
// Here is some example hard-coded return values:
var winsize = {width: 600, height: 400};
// You can change these numbers to see how they influence
// the outcome.
// var centerpos = cc.p(winsize.width / 2, winsize.height / 2);
// This line doesn't seem relevant for the question you asked.
// Or, rather, the following calculations will result in the tiles
// being centred on the screen anyway, so this calculation here
// is unnecessary.
// Being a chess board, I assume that you want the tiles to be square,
// i.e. to have the same width and height.
// If so, first find out which is the minimum dimension
// and calculate the tile size as being 1/8 of that.
var minDimn = Math.min(winsize.width, winsize.height);
var tileSize = minDimn / 8;
// Find out how far in from the left and how far down from the top
// you need the upper left corner of the upper left tile to start.
// This assumes that you don't need any "margin" around the board.
// (If you do need such a "margin", basically subtract it twice
// from each of winsize.width and winsize.height above.)
// Start with default values of 0 for each, but then add in the
// excess for the longer dimension, but divide it by two
// because that excess will be split between either
// the top and bottom or the left and right.
var xStart = 0, yStart = 0;
if (winsize.width > winsize.height) {
xStart = (winsize.width - winsize.height) / 2;
} else if (winsize.height > winsize.width) {
yStart = (winsize.height - winsize.width) / 2;
}
// Instead of looping through all 64 positions in one loop,
// loop through all the horizontal positions in an outer loop
// and all the vertical positions in an inner loop.
for (i = 0; i < 8; i++) {
// For the horizontal dimension, calculate x for each tile
// as the starting position of the left-most tile plus
// the width of the tile multiplied by the number of tiles (0-based)
var x = xStart + i * tileSize;
// Now the inner loop
for (j = 0; j < 8; j++) {
// Same type of calculation for the y value.
var y = yStart + j * tileSize;
// You can see the values in this demo here.
document.write("<pre>(" + x + ", " + y + ")</pre>");
// The following two lines don't seem to be relevant to the question.
// var tile = cc.Sprite.create(res.tile_png);
// this.addChild(tile,0);
// Use your calculated values in your function call.
// tile.setPosition(x, y);
}
}

randomly mapping divs

I am creating a new "whack-a-mole" style game where the children have to hit the correct numbers in accordance to the question. So far it is going really well, I have a timer, count the right and wrong answers and when the game is started I have a number of divs called "characters" that appear in the container randomly at set times.
The problem I am having is that because it is completely random, sometimes the "characters" appear overlapped with one another. Is there a way to organize them so that they appear in set places in the container and don't overlap when they appear.
Here I have the code that maps the divs to the container..
function randomFromTo(from, to) {
return Math.floor(Math.random() * (to - from + 1) + from);
}
function scramble() {
var children = $('#container').children();
var randomId = randomFromTo(1, children.length);
moveRandom('char' + randomId);
}
function moveRandom(id) {
var cPos = $('#container').offset();
var cHeight = $('#container').height();
var cWidth = $('#container').width();
var pad = parseInt($('#container').css('padding-top').replace('px', ''));
var bHeight = $('#' + id).height();
var bWidth = $('#' + id).width();
maxY = cPos.top + cHeight - bHeight - pad;
maxX = cPos.left + cWidth - bWidth - pad;
minY = cPos.top + pad;
minX = cPos.left + pad;
newY = randomFromTo(minY, maxY);
newX = randomFromTo(minX, maxX);
$('#' + id).css({
top: newY,
left: newX
}).fadeIn(100, function () {
setTimeout(function () {
$('#' + id).fadeOut(100);
window.cont++;
}, 1000);
});
I have a fiddle if it helps.. http://jsfiddle.net/pUwKb/8/
As #aug suggests, you should know where you cannot place things at draw-time, and only place them at valid positions. The easiest way to do this is to keep currently-occupied positions handy to check them against proposed locations.
I suggest something like
// locations of current divs; elements like {x: 10, y: 40}
var boxes = [];
// p point; b box top-left corner; w and h width and height
function inside(p, w, h, b) {
return (p.x >= b.x) && (p.y >= b.y) && (p.x < b.x + w) && (p.y < b.y + h);
}
// a and b box top-left corners; w and h width and height; m is margin
function overlaps(a, b, w, h, m) {
var corners = [a, {x:a.x+w, y:a.y}, {x:a.x, y:a.y+h}, {x:a.x+w, y:a.y+h}];
var bWithMargins = {x:b.x-m, y:b.y-m};
for (var i=0; i<corners.length; i++) {
if (inside(corners[i], bWithMargins, w+2*m, h+2*m) return true;
}
return false;
}
// when placing a new piece
var box;
while (box === undefined) {
box = createRandomPosition(); // returns something like {x: 15, y: 92}
for (var i=0; i<boxes.length; i++) {
if (overlaps(box, boxes[i], boxwidth, boxheight, margin)) {
box = undefined;
break;
}
}
}
boxes.push(box);
Warning: untested code, beware the typos.
The basic idea you will have to implement is that when a random coordinate is chosen, theoretically you SHOULD know the boundaries of what is not permissible and your program should know not to choose those places (whether you find an algorithm or way of simply disregarding those ranges or your program constantly checks to make sure that the number chosen isn't within the boundary is up to you. the latter is easier to implement but is a bad way of going about it simply because you are entirely relying on chance).
Let's say for example coordinate 50, 70 is selected. If the picture is 50x50 in size, the range of what is allowed would exclude not only the dimensions of the picture, but also 50px in all directions of the picture so that no overlap may occur.
Hope this helps. If I have time, I might try to code an example but I hope this answers the conceptual aspect of the question if that is what you were having trouble with.
Oh and btw forgot to say really great job on this program. It looks awesome :)
You can approach this problem in at least two ways (these two are popped up in my head).
How about to create a 2 dimensional grid segmentation based on the number of questions, the sizes of the question panel and an array holding the position of each question coordinates and then on each time frame to position randomly these panels on one of the allowed coordinates.
Note: read this article for further information: http://eloquentjavascript.net/chapter8.html
The second approach follow the same principle, but this time to check if the panel overlap the existing panel before you place it on the canvas.
var _grids;
var GRID_SIZE = 20 //a constant holding the panel size;
function createGrids() {
_grids = new Array();
for (var i = 0; i< stage.stageWidth / GRID_SIZE; i++) {
_grids[i] = new Array();
for (var j = 0; j< stage.stageHeight / GRID_SIZE; j++) {
_grids[i][j] = new Array();
}
}
}
Then on a separate function to create the collision check. I've created a gist for collision check in Actionscript, but you can use the same principle in Javascript too. I've created this gist for inspirational purposes.
Just use a random number which is based on the width of your board and then modulo with the height...
You get a cell which is where you can put the mole.
For the positions the x and y should never change as you have 9 spots lets say where the mole could pop up.
x x x
x x x
x x x
Each cell would be sized based on % rather then pixels and would allow re sizing the screen
1%3 = 1 (x)
3%3 = 0 (y)
Then no overlap is possible.
Once the mole is positioned it can be show or hidden or moved etc based on some extended logic if required.
If want to keep things your way and you just need a quick re-position algorithm... just set the NE to the SW if the X + width >= x of the character you want to check by setting the x = y+height of the item which overlaps. You could also enforce that logic in the drawing routine by caching the last x and ensuring the random number was not < last + width of the item.
newY = randomFromTo(minY, maxY);
newX = randomFromTo(minX, maxX); if(newX > lastX + characterWidth){ /*needful*/}
There could still however be overlap...
If you wanted to totally eliminate it you would need to keep track of state such as where each x was and then iterate that list to find a new position or position them first and then all them to move about randomly without intersecting which would would be able to control with just padding from that point.
Overall I think it would be easier to just keep X starting at 0 and then and then increment until you are at a X + character width > greater then the width of the board. Then just increase Y by character height and Set X = 0 or character width or some other offset.
newX = 0; newX += characterWidth; if(newX + chracterWidth > boardWidth) newX=0; newY+= characterHeight;
That results in no overlap and having nothing to iterate or keep track of additional to what you do now, the only downside is the pattern of the displayed characters being 'checker board style' or right next to each other (with possible random spacing in between horizontal and vertical placement e.g. you could adjust the padding randomly if you wanted too)
It's the whole random thing in the first place that adds the complexity.
AND I updated your fiddle to prove I eliminated the random and stopped the overlap :)
http://jsfiddle.net/pUwKb/51/

Categories