I have a kind of "bouncing balls" project, where I draw 150 particles at a canvas and, at every redraw, it recalc particles position, and verify if any particles is at a corner, to invert its iterator.
But thing is that this project has a factor that not all "bouncing balls" projects have. The balls need to bounce within the bounds of a map.
So, when I create the canvas, I also use a SVG to iterate over the pixels and create a array of every bound in the x axis, left and right, so my particles will know exactly where they need to rebounce.
So good, so well, it is working nice, but my canvas is 500px tall, so it need to iterate 500 times, with a lot of conditionals to prevent weird behavior, this multiplied by 150 particles, and in every redraw.
It has became very performance greedy and I need to improve performance, so, here is my collision system code
const colisionSystem = state => {
for (var b=0, hs=state.bounds.length; b<hs; b++) {
if(
state.bounds[b][0]
&& state.x - state.radius < state.bounds[b][0].x
&& state.y + state.radius > state.bounds[b][0].y
&& state.y - state.radius < state.bounds[b][0].y
) {
if (
state.bounds[b][0].x > 0
&& state.bounds[b][0].x < (state.widgetSize.width * 0.33)
&& state.bounds[b][0].y > (state.widgetSize.height * 0.33)
&& state.bounds[b][0].y < (state.widgetSize.width * 0.45)
) {
// middle left bottom corner at acre
state.x = state.radius + state.bounds[b][0].x;
state.vy *= -1;
} else if (
state.bounds[b][0].x > 0
&& state.bounds[b][0].x < (state.widgetSize.width * 0.098)
&& state.bounds[b][0].y > (state.widgetSize.height * 0.167)
&& state.bounds[b][0].y < (state.widgetSize.width * 0.206)
) {
// middle left top corner at acre
state.y = state.radius + state.bounds[b][0].y + 1;
state.vx *= -1;
state.vy *= -1;
} else {
state.x = state.radius + state.bounds[b][0].x;
state.vx *= -1;
}
if(state.oldAxis === state.x) {
state.y = state.y - 1;
} else {
state.oldAxis = state.x;
}
state.antiRebounce = false;
}
if(
state.bounds[b][1]
&& state.x + state.radius > state.bounds[b][1].x
&& state.y + state.radius > state.bounds[b][1].y
&& state.y - state.radius < state.bounds[b][1].y
) {
if (
state.bounds[b][1].x > (state.widgetSize.width * 0.555)
&& state.bounds[b][1].x < (state.widgetSize.width * 0.983)
&& state.bounds[b][1].y > 0
&& state.bounds[b][1].y < (state.widgetSize.width * 0.2098)
) {
// Top right corner
if(state.antiRebounce) {
state.vy *= -1;
state.antiRebounce = false;
} else {
state.antiRebounce = true;
}
state.y = state.bounds[b][1].y + state.radius + 1;
state.vy *= -1;
}
if (
state.bounds[b][1].x > (state.widgetSize.width * 0.604)
&& state.bounds[b][1].x < (state.widgetSize.width * 0.827)
&& state.bounds[b][1].y > (state.widgetSize.width * 0.665)
&& state.bounds[b][1].y < (state.widgetSize.width * 0.778)
) {
// bottom right corner
state.vy *= -1;
} else {
state.vx *= -1;
state.x = state.bounds[b][1].x - state.radius;
}
if(state.oldAxis === state.x) {
state.y = state.y - 1;
} else {
state.oldAxis = state.x;
}
}
}
if (state.y + state.radius > state.widgetSize.height) {
state.vy *= -1;
state.y = state.widgetSize.height - state.radius;
}
if (state.y - state.radius < 0) {
state.vy *= -1;
state.y = state.radius;
}
return state;
}
export default colisionSystem;
So, question is, is there any practical advice to improve this code itself?
You have 500 * 150 particles (750000) that is a lot and frankly too much for a JS app to handle. (BUT you say 150 particles so I am somewhat confused as to what you are doing)
To increase the performance of the function provided you can use a few simple rules of thumb.
An array lookup is slower than a direct referance.
ie
// a compound referance
state.bounds[b][1].x // find state, then bounds, then index b, then 1,then x
// if you have the common parts used frequently
var b1 = state.bounds[b][1]; // does all the lookups
b1.x; // but now only needs one lookup to get x
Same is true for item properties. Each . means an additional lookup. You can get a lot of extra performance by creating temporary variables to hold the result of all the lookups.
Applying this to your code and you get
const colisionSystem = state => {
var w = state.widgetSize.width; // used many times in the loop
var h = state.widgetSize.height;
var x = state.x;
var y = state.y;
var r = state.radius;
for (var b = 0, hs = state.bounds.length; b < hs; b++) {
var bounds = state.bounds[b];
if (bounds[0]){
var b0 = bounds[0];
if( x - r < b0.x && y + r > b0.y && y - r < b0.y) {
if ( b0.x > 0 && b0.x < (w * 0.33) && b0.y > (h * 0.33) && b0.y < (w * 0.45)) {
x = r + b0.x; // middle left bottom corner at acre
state.vy *= -1;
} else if ( b0.x > 0 && b0.x < (w * 0.098) && b0.y > (h * 0.167) && b0.y < (w * 0.206)) {
y = r + b0.y + 1; // middle left top corner at acre
state.vx *= -1;
state.vy *= -1;
} else {
x = r + b0.x;
state.vx *= -1;
}
if (state.oldAxis === x) {
y -= 1;
} else {
state.oldAxis = x;
}
state.antiRebounce = false;
}
}
if (bounds[1]){
var b1 = bounds[1];
if( x + r > b1.x && y + r > b1.y && y - r < b1.y) {
if ( b1.x > (w * 0.555) && b1.x < (w * 0.983) && b1.y > 0 && b1.y < (w * 0.2098)) {
if (state.antiRebounce) { // Top right corner
state.vy *= -1;
state.antiRebounce = false;
} else {
state.antiRebounce = true;
}
y = b1.y + r + 1;
state.vy *= -1;
}
if (b1.x > (w * 0.604) && b1.x < (w * 0.827) && b1.y > (w * 0.665) && b1.y < (w * 0.778)) {
state.vy *= -1; // bottom right corner
} else {
state.vx *= -1;
x = b1.x - r;
}
if (state.oldAxis === x) {
y = y - 1;
} else {
state.oldAxis = x;
}
}
}
}
if (y + r > h) {
state.vy *= -1;
y = h - r;
} else if (y - r < 0) { // added else. Cant both happen at the same time?????
state.vy *= -1;
y = r;
}
state.y = y; // set state x,y to reflect any changes.
state.x = x;
return state;
}
By replacing the many compound references with direct references you can free up a lot of CPU time, but this will depend on the data. If the code above spends more time rejecting the conditional statements early you will not get much of a benefit, and if the bounds[1],bounds[0] conditions are only passed once a call to colisionSystem you will get no benefit, and maybe a slight decrease in performance. Same holds true for the number of items in the loop, if the loop is iterating many items you will see an improvement, if the loop is only 1 or 2 items you will see no benefit and for one item you will see a decrease in performance.
Note, I was not careful refactoring, there may be some typos in above code and is only an example.
You say you use SVG to iterate???
I also use a SVG to iterate over the pixels
Rule of thumb #2. The DOM is slow, SVG is part of the DOM and in my book SVG is really VSG (Very Slow Graphics) and I would say that the main part of the slow down is in whatever you do with the SVG.
Recommendations:
This function is doing way too much, consider breaking this up so that expressions like this
state.bounds[b][1].x > (state.widgetSize.width * 0.604)
&& state.bounds[b][1].x < (state.widgetSize.width * 0.827)
&& state.bounds[b][1].y > (state.widgetSize.width * 0.665)
&& state.bounds[b][1].y < (state.widgetSize.width * 0.778)
Are their own function
Consider using _.map instead of the for loop, you are return state anyway, so _.map would be more semantic.
Consider breaking up the nesting of ifs with functions. Every time you use a code comment, consider making it a function. For example
// Top right corner
if(state.antiRebounce) {
state.vy *= -1;
state.antiRebounce = false;
} else {
state.antiRebounce = true;
}
state.y = state.bounds[b][1].y + state.radius + 1;
state.vy *= -1;
could be
const topRightCorner = (state) => {
if(state.antiRebounce) {
state.vy *= -1;
state.antiRebounce = false;
} else {
state.antiRebounce = true;
}
state.y = state.bounds[b][1].y + state.radius + 1;
state.vy *= -1;
return state;
}
Once you broken this giant function into many smaller easier to understand functions. You can use chrome profiling tools to find bottlenecks for performance.
https://developers.google.com/web/tools/chrome-devtools/rendering-tools/
With the code broken up, it will be easy to see what part of the script is suffering from performance problems, at that point you can see about how to fix, without doing pre-mature optimization.
AS far as I know (but I don't know so much) your situation doesn't give us so much space of manovre because you need to "see" always in the screen all that bouncing objects. The bound system seems good for me checking for the bounds before a more accurate point collision detection.
How many fps are you trying to render? Maybe something could be tuned I guess.
Bye
Related
I'm learning some canvas stuff and am making a personal project with HTML/JS so pardon my ugly inelegant code.
In the code below, when only one of the a/b/c/d functions is called, everything works fine; but if more than one is called, then the canvas starts to freak out with weird flickering colours. No error messages could be found in the console. The problem seems to subside when I decrease the number of tiles to be drawn but not the canvas size itself. I did call the canvas draw function a lot in other sections of the code so could these functions be the straw that broke the memory limit or something?
Help would be much appreciated! Screenshot
for (let y = 0; y < yCount; y++) {
for (let x = 0; x < xCount; x++) {
if (tileList[y][x] == 1) {
a(x, y);
b(x, y);
c(x, y);
d(x, y);
}
}
}
function a(x, y) {
if (x != 0 && y != 0) { //! TOP-LEFT
if (tileList[y][x - 1] != 1 && tileList[y - 1][x] != 1) {
subfunctionTopLeft(x, y, palette[0]);
}
}
}
function b(x, y) {
if (x != xCount - 1 && y != 0) { //! TOP-RIGHT
if (tileList[y][x + 1] != 1 && tileList[y - 1][x] != 1) {
subfunctionTopRight(x, y, palette[0]);
}
}
}
function c(x, y) {
if (x != 0 && y != yCount - 1) { //! BOTTOM-LEFT
if (tileList[y][x - 1] != 1 && tileList[y + 1][x] != 1) {
subfunctionBottomLeft(x, y, palette[0]);
}
}
}
function d(x, y) {
if (x != xCount - 1 && y != yCount - 1) { //! BOTTOM-RIGHT
if (tileList[y + 1][x] != 1 && tileList[y][x + 1] != 1) {
subfunctionBottomRight(x, y, palette[0]);
}
}
}
(The subfunctions are some custom draw functions.)
We have to program a JavaScript version of Conway's Game of Life for a school project, but we're stuck on looping the edges. The whole thing works fine, but the function that calculates the number of neighbors doesn't work on the cells that are on the edges (because it has to evaluate values outside of the array, which are undefined). We've tried several options, but they all alter the functionality of the rest of the program.
What should we add for it to work on the edges of the grid?
var totalNeighbors = function(x, y) {
var total = 0;
if (x > 0 && cells[(x - 1)][y] == 1) {
total++;
}
if (x < (width - 1) && cells[x + 1][y] == 1) {
total++;
}
if (y > 0 && cells[x][y - 1] == 1) {
total++;
}
if (y < (height - 1) && cells[x][y + 1] == 1) {
total++;
}
if (y > 0 && x > 0 && cells[x - 1][y - 1] == 1) {
total++;
}
if (y > 0 && x < (width - 1) && cells[x + 1][y - 1] == 1) {
total++;
}
if (y < (height - 1) && x > 0 && cells[x - 1][y + 1] == 1) {
total++;
}
if (y < (height - 1) && x < (width - 1) && cells[x + 1][y + 1] == 1) {
total++;
}
return total;
};
Thanks!
I'd go with something more like this:
As you can see, I refactored a little bit.
var isvalid = function(x, y) {
/*
* This returns 1 if cells[x][y] == 1.
* Otherwise, we return 0.
* NOTE: If cells[x, y] is out of bounds, we return 0.
* GLOBALS USED: cells, width, and height.
*/
//This returns true if (index < size && index >= 0)
//Used to check that index is not an invalid index.
var inbounds = function (size, index) {
return (index >= 0 && index < size);
};
//given point is out of bounds
if (!inbounds(width, x) || !inbounds(height, y)) {
return 0;
}
//everything is good
return (cells[x][y] === 1) ? 1 : 0;
};
var totalNeighbors = function(x, y) {
var total = 0;
//cells[x-1][y]
total += isvalid(x-1, y);
//cells[x + 1][y]
total += isvalid(x+1, y);
//cells[x][y - 1]
total += isvalid(x, y-1);
//cells[x][y + 1]
total += isvalid(x, y+1);
//cells[x - 1][y - 1]
total += isvalid(x-1, y-1);
//cells[x + 1][y - 1]
total += isvalid(x+1, y-1);
//cells[x - 1][y + 1]
total += isvalid(x-1, y+1);
//cells[x + 1][y + 1]
total += isvalid(x+1, y+1);
return total;
};
PS: Your original code sample is 37 lines without comments. My code sample is 52 lines with comments and 33 lines without comments.
As near as I can figure, this way is cleaner and shorter. ;)
I have an if else statement, I was wondering if I could change it into a for loop or something similar.
if (lastScroll >= 0 && lastScroll < 40) {
pos = 0;
} else if (lastScroll >= 40 && lastScroll < 80) {
pos = 1;
} else if (lastScroll >= 80 && lastScroll < 120) {
pos = 2;
} else if (lastScroll >= 120 && lastScroll < 160) {
pos = 3;
} else if (lastScroll >= 160 && lastScroll < 200) {
pos = 4;
} else if (lastScroll > 200) {
pos = 5;
}
I want to change this because there could be over 100 positions. I was thinking about creating a for loop like this:
var i = 0;
greater = 0;
less = 40;
for (i = 0; i < 100; i++) {
if (lastScroll >= greater && lastScroll < less) {
pos = i;
greater += 40;
less += 40;
}
}
The if else statement works perfectly but I don't want to create 100 if else statements. This is wrapped in a scroll function.
Because it's linear you can use division and rounding
pos = Math.floor(lastScroll / 40);
if (pos > 5) pos = 5;
You don't need a loop. You can do the following:
if( lastScroll > 200 ) {
pos = 5;
} else if ( lastScroll >= 0 ) {
pos = Math.floor( lastScroll / 40 );
}
If you want, you could potentially scrap everything and use
pos = int(lastscroll / 40);
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
i am writing a code for conway game of life...... i am taking 2> arrays one fr old generation. and one for 2 nd genration.
**rules are : The universe of the Game of Life is an infinite two-dimensional orthogonal grid of square cells, each of which is in one of two possible states, alive or dead. Every cell interacts with its eight neighbours, which are the cells that are horizontally, vertically, or diagonally adjacent. At each step in time, the following transitions occur: **1.Any live cell with fewer than two live neighbours dies, as if caused by under-population.
2.Any live cell with two or three live neighbours lives on to the next generation.
3.Any live cell with more than three live neighbours dies, as if by overcrowding.
4.Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.**
The initial pattern constitutes the seed of the system. The first generation is created by applying the above rules simultaneously to every cell in the seed—births and deaths occur simultaneously, and the discrete moment at which this happens is sometimes called a tick (in other words, each generation is a pure function of the preceding one). The rules continue to be applied repeatedly to create further generations.**
here is the code
I am getting a soln but i guess its not giving me the correct solution becuase its not checking the neighbors of the corners. i have marked that part
**
window.conway =
{
};
window.conway.maingame =
{
};
conway.maingame = function(width, height)
{
window.a = [];
this.width = width;
this.height = height;
this.map = new Array(width);
for( i = 0; i < this.width; i++)
{
this.map[i] = new Array(height);
}
console.log(this.map, "map")
}
conway.maingame.prototype.randomize = function()
{
for( y = 0; y < this.height; y++)
{
//console.log("enter for loop")
for( x = 0; x < this.width; x++)
{
if(Math.random() > .5)
{
i =true;
}
else
{
i = false;
}
//console.log("enter function")
this.set(x, y, i);
}
}
}
conway.maingame.prototype.set = function(x, y, val)
{
x = x % this.width;
y = y % this.height;
this.map[x][y] = val;
console.log(this.map, "map2");
}
conway.maingame.prototype.get = function(x, y)
{
x = x % this.width;
y = y % this.height;
return this.map[x][y];
}
*********************************************************************************
conway.maingame.prototype.neighbors = function(x, y)
{
count = 0;
if(x > 0 && y > 0 && this.get(x + 1, y + 1))
{
console.log(this.get(x + 1, y + 1), "value neighbor");
count++;
console.log(count);
}
if(x > 0 && y > 0 && this.get(x + 1, y))
{
console.log(this.get(x + 1, y), "vallue neighbor");
count++;
console.log(count);
}
if(x > 0 && y > 0 && this.get(x + 1, y - 1))
{
console.log(this.get(x + 1, y - 1), "vallue neighbor");
count++;
console.log(count);
}
if(x > 0 && y >=0 && this.get(x, y - 1))
{
console.log(this.get(x + 1, y - 1), "vallue neighbor");
count++;
console.log(count);
}
if(x > 0 && y > 0 && this.get(x - 1, y - 1))
{
console.log(this.get(x + 1, y - 1), "vallue neighbor");
count++;
console.log(count);
}
if(x > 0 && y > 0 && this.get(x - 1, y))
{
console.log(this.get(x + 1, y - 1), "vallue neighbor");
count++;
console.log(count);
}
if(x > 0 && y > 0 && this.get(x - 1, y + 1))
{
console.log(this.get(x + 1, y - 1), "vallue neighbor");
count++;
console.log(count);
}
if(x > 0 && y > 0 &&this.get(x, y + 1))
{
console.log(this.get(x + 1, y - 1), "vallue neighbor");
count++;
console.log(count);
}
return count;
}***
conway.maingame.prototype.newgeneration = function()
{
var newMap = new Array(this.width);
for( i = 0; i < this.width; i++)
{
newMap[i] = new Array(this.height);
}
for(var y = 0; y < this.height; y++)
{
for(var x = 0; x < this.width; x++)
{
console.log("enter all for");
newMap[x][y] = this.get(x, y);
console.log(newMap, "newarray");
//Rule 1: any live cell with fewer than two live neighbors dies
if(this.get(x, y) == true && this.neighbors(x, y) < 2)
{
newMap[x][y] = false;
console.log("rule1");
}
//Rule 2: Any live cell with two or three live neighbours lives on to the next generation
if(this.get(x, y) == true && this.neighbors(x, y) == 2 || this.neighbors(x, y) == 3)
{
newMap[x][y] = true
console.log("rule2");
}
//Rule 3: any live cell with more than three live neighbors dies
if(this.get(x, y) == true && this.neighbors(x, y) > 3)
{
newMap[x][y] = false;
console.log("rule3");
}
//Rule 4: any dead cell with exactly three live neighbors becomes a live cell
if(this.get(x, y) == false && this.neighbors(x, y) == 3)
{
newMap[x][y] = true;
console.log("rule4");
}
}
}
this.map = newMap;
console.log(this.map,"new generation")
}
**
I trawled through your code in JSHint and fixed all of the issues, wrote some glue to test it in the browser and here's the result: jsfiddle.
It looks like it's working to me, so I think the problem must have been due to one of the dozens of warnings that JSHint flagged up.
Re: your assertion that the issue was due to the corners, that's what these lines are for:
x = x % this.width;
y = y % this.height;
In my test case I'm using a 10 x 10, so when it comes to check the neighbours of (9,9) it looks at (10, 10) and (10 % 10, 10 % 10) is (0, 0), thus avoiding looking outside the array. I believe this is what's known as a toroidal array.
The lesson we learn from this? Keep on top of the small issues, and the big issues will take care of themselves.
I was starting to teach myself how to work with HTML5 Canvas and decided to learn by making a short game/demo.
I wanted to make a simple blocks bounce around the screen, bounce off the walls, and bounce off each other.
I'm stuck on getting them to bounce off each other. It seems like code that makes it bounce away is making it bounce back immediately after. I see where the code fails but I don't know how to fix it :( Can anyone help?
(Side question: I know I'm not working as clean/efficiently/professionally as possible in this example but if I wanted to improve with feedback and opinions about the 'best' method for this type of example, like a code review or something, is it ok to ask a question on stackoverflow?)
jsfiddle:
http://jsfiddle.net/vdcSv/
HTML:
<canvas id="canvas" Width="400" Height="300"></canvas>
Javscript:
function CheckBallCollision(BallsArray, index) {
for (var i = 0; i < BallsArray.length; i++) {
if (index != i) {
if (BallsArray[index].Xdir == 1) {
if ((BallsArray[index].Xmax >= BallsArray[i].Xmin)) {
if ((BallsArray[index].Ymin <= BallsArray[i].Ymin) && (BallsArray[index].Ymax >= BallsArray[i].Ymin) ||
((BallsArray[index].Ymax >= BallsArray[i].Ymax) && (BallsArray[index].Ymin <= BallsArray[i].Ymax))) {
BallsArray[index].Xdir = -BallsArray[index].Xdir;
}
}
} else if (BallsArray[index].Xdir == -1) {
if ((BallsArray[index].Xmin <= BallsArray[i].Xmax)) {
if ((BallsArray[index].Ymin <= BallsArray[i].Ymin) && (BallsArray[index].Ymax >= BallsArray[i].Ymin) ||
((BallsArray[index].Ymax >= BallsArray[i].Ymax) && (BallsArray[index].Ymin <= BallsArray[i].Ymax))) {
BallsArray[index].Xdir = -BallsArray[index].Xdir;
}
}
}
}
}
}
Ball Object:
function Ball() {
this.Xmin = 0;//top left X coord
this.Ymin = 0;//top left y coord
this.Height = 25;
this.Width = 25;
this.Xmax = this.Xmin + this.Width;
this.Ymax = this.Ymin + this.Height;
this.Xdir = 0; // 0 not moving, 1 moving right, -1 moving left
this.Ydir = 0;
this.Red = 0;
this.Green = 0;
this.Blue = 200;
this.Opacity = 1;
this.Speed = 1;
}
Got it working by changing the <= to ==
It's messy and things often miss the necessary bounce off a block :( I'm sure part of the reason is falling back on the == instead of <=. If anyone has a better solution - I'm all ears :)
http://jsfiddle.net/vdcSv/1/
function CheckBallCollision(BallsArray, index) {
for (var i = 0; i < BallsArray.length; i++) {
if (index != i) {
if (BallsArray[index].Xdir == 1) {
if ((BallsArray[index].Xmax == BallsArray[i].Xmin)) {
if ((BallsArray[index].Ymin <= BallsArray[i].Ymin) && (BallsArray[index].Ymax >= BallsArray[i].Ymin) ||
((BallsArray[index].Ymax >= BallsArray[i].Ymax) && (BallsArray[index].Ymin <= BallsArray[i].Ymax))) {
BallsArray[index].Xdir = -BallsArray[index].Xdir;
}
}
} else if (BallsArray[index].Xdir == -1) {
if ((BallsArray[index].Xmin == BallsArray[i].Xmax)) {
if ((BallsArray[index].Ymin <= BallsArray[i].Ymin) && (BallsArray[index].Ymax >= BallsArray[i].Ymin) ||
((BallsArray[index].Ymax >= BallsArray[i].Ymax) && (BallsArray[index].Ymin <= BallsArray[i].Ymax))) {
BallsArray[index].Xdir = -BallsArray[index].Xdir;
}
}
}
}
}
}
Here are a couple hit detection snippets you might want to look into:
ball.hitTestCircle = function(obj) {
var dx = this.x - obj.x;
var dy = this.y - obj.y;
var distance = (dx * dx) + (dy * dy);
var area = (this.radius + obj.radius)*(this.radius + obj.radius);
return (area / distance);
};
if the call returns 1 or greater your colliding, and you can even use that info to fix the difference.
Here is basic rect hit detections script:
ball.hitTestRect = function(b) {
var difference = {};
difference.x = this.x - b.x - b.width;
difference.y = this.y - b.y - b.height;
difference.height = this.height + b.height;
difference.width = this.width + b.width;
if (difference.x < 0 && difference.y <= 0 && difference.height + difference.y >= 0 && difference.width + difference.x >= 0) return true;
return false;
};
I would call either of these with something like :
for(var i=0;i!=balls.length;i++){
for(var j=0;j!=balls.length;j++){
if(j!=i){
if(balls[i].hitTestRect(balls[j])){
// all your reversing motion code
}
}
}
}
It looks like you forgot to check if
BallsArray[index].Xmin <= BallsArray[i].Xmax)
If you add this in it works. It's also worth noting that you don't need different code for the two different X directions as this behaviour is symmetrical. Regardless of which way it is travelling to begin with you have it reversing direction. It's also symmetrical in the Y direction so if you just add:
BallsArray[index].Ydir = -BallsArray[index].Ydir;
to the 'then' part of the if you'll only need one if to take care of all four kinds of collisions.
You may also want to add a break statement so that if a ball happens to collide with two other balls at the same time it will only reverse direction once.
For a more realistic simulation you can multiply by a negative number in the (0, 1) interval, however if you don't do something else your system will slowly settle into a steady state until the rounding errors kick in and it freaks out.