I am attaching a waypoint to 2000 elements on the page, because I want to trigger a different event for each one. I have tried the noframework way as well as using jquery, and each one adds about 20 seconds to the loading of my page. Is there a technique I can use to attach waypoints to a lot of elements without it slowing down my page load? Thanks.
Jquery version (call once):
var waypoints = $('[data-waypointidentifier="iswaypoint"]').waypoint({
handler: function (direction) {
waypointHandler(direction, this);
}
});
No Framework version (called once for each of the 2000 elements):
var waypoint = new Waypoint({
document.getElementById('element-waypoint1'),
handler: function (direction) {
waypointHandler(direction, this);
}
});
This is not really an answer BUT does demonstrate the issue in near lowest form.
EDIT: This is perhaps a bit of a workaround hack now. Modified to simply attach in chunks.
NOTE: you will have to modify the last chunk - mine had even numbers at 2000 which I "chunked" in groups and then set a bound timeout to attach them. It still takes just as long but makes the situation slightly less visible while/until they all attach.
// approx times to attach (not definitive)
// - note each increment nearly doubles times, not linear
// super simple test, chrome, prob more on most computers
// var punchCount = 250; // 150ms
// var punchCount = 500; // 500ms
// var punchCount = 1000; // 2350ms
// var punchCount = 1500; // 4700ms
var punchCount = 2000; // 8750ms
function initme() {
var addme = $('<div class="facepunch" data-iam="-1">hi</div>');
var addgroup = $('<div>start</div>');
for (var i = punchCount; i > 0; i--) {
addme = $('<div class="facepunch">hi</div>').data('iam', i);
addme.appendTo(addgroup);
}
$('#punchme').append(addgroup);
}
var t0 = performance.now();
console.log('init 1');
initme();
var t1 = performance.now();
console.log("Call to initme took " + (t1 - t0) + " milliseconds.");
console.log('before 1');
function waypointHandler(direction, me) {
// NOOO... this will kill your perf console.log(direction, me);
var newt = "howdy " + $(me.element).data('iam');
$(me.element).text(newt + " " + direction);
}
t0 = performance.now();
console.log('before 2');
var waypoints = {};
function createWaysChunk(begin, end) {
console.log(begin, end, $('.facepunch').slice(begin, end).length);
$('.facepunch').slice(begin, end).waypoint({
// enabled: false,//no perf benefit
handler: function(direction) {
waypointHandler(direction, this); // "this.element" is the element
}
});
}
function createWays() {
t0 = performance.now();
var chunksize = 40;
var y = punchCount / chunksize; // 250
var x = 0;
for (var i = 0; i < chunksize; ++i) {
begin = x;
end = y * i;
setTimeout(createWaysChunk.bind(null, begin, end), 100);
x = end;
}
t1 = performance.now();
console.log("Call to createways = took " + (t1 - t0) + " milliseconds.");
}
// time out just moves the "freeze" to after the page loads
//var timeoutID0 = window.setTimeout(createWays, 2000);
window.onload = createWays;
// createWays();
t1 = performance.now();
console.log("Call to waypoints = took " + (t1 - t0) + " milliseconds.");
// Test with .enableAll
//t0 = performance.now();
//console.log('after 1');
// time out just moves the "freeze" to after the page loads
//var timeoutID = window.setTimeout(Waypoint.enableAll, 2000);
//initial enabled = false then enable is way slow, slower than true overall by 15000+ ms
//Waypoint.enableAll();
//console.log('after 2');
//t1 = performance.now();
//console.log("Call to enableAll took " + (t1 - t0) + " milliseconds.");
.facepunch {
height: 3em;
border: solid 1px lime;
margin: 0.25em;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/waypoints/4.0.1/jquery.waypoints.min.js" integrity="sha256-jDnOKIOq2KNsQZTcBTEnsp76FnfMEttF6AV2DF2fFNE=" crossorigin="anonymous"></script>
<div id="punchme"></div>
Related
I have created a simple function that would "animate" the cell backcolor at a tap, it works perfectly fine:
Color nOldColor = _grid.BackgroundColor;
for (int i = 0; i <= 100; i += 5)
{
double f = (double)i / (double)100;
Color nNewColor = PCLHelper.BlendColors(nOldColor, Color.Red, f);
_grid.BackgroundColor = nNewColor;
_label1.BackgroundColor = nNewColor;
await Task.Delay(5);
}
_grid.BackgroundColor = nOldColor;
_label1.BackgroundColor = nOldColor;
Now I wanted to do the same with an Animation, but the animation doesn't show the steps "in-between" but rather (as it looks to me) switches to the final color:
private async void animateButtonTouched()
{
int repeatCountMax = 100;
Color nOldColor = _grid.BackgroundColor;
var repeatCount = 0;
_grid.Animate("changeBG", new Animation((val) =>
{
double f = (double)repeatCount / (double)100;
Color nNewColor = PCLHelper.BlendColors(nOldColor, Color.Red, f);
_grid.BackgroundColor = nNewColor;
_label1.BackgroundColor = nNewColor;
}),
5, //duration. I've also tried it with 100. Nothing helped
finished: (val, b) =>
{
repeatCount++;
}, repeat: () =>
{
return repeatCount < repeatCountMax;
});
What am I doing wrong?
"You are making it more difficult than it needs to be." Trademark pending 🍣
The Animate callback is providing the stepping value (or keyframe value). This is a double from 0 to 1 that is called ever X milliseconds (i.e. the length of a single animation frame, 16 default) over the course of X milliseconds (250 default).
So in this example the ShiftToColor gets called 125 times (2000 / 16) with a value that is evenly divided from 0 to 1, thus steps of .008.
var orgColor = aFormsElementInstance.BackgroundColor;
aFormsElementInstance.Animate("changeBG", new Animation((val) =>
{
Color ShiftToColor(Color From, Color To, double pct)
{
var r = From.R + ((To.R - From.R) * val);
var g = From.G + ((To.G - From.G) * val);
var b = From.B + ((To.B - From.B) * val);
return new Color(r, g, b);
}
Device.BeginInvokeOnMainThread(() =>
{
aFormsElementInstance.BackgroundColor = ShiftToColor(orgColor, Color.Red, val);
});
}), 16, 2000);
Results in:
I've deliberately provided the version number in the question as this is the kind of question that will go out of date at some point.
Here is a simple animation that will run perfectly smoothly in Chrome and Safari, but will be very jerky in Firefox:
function lerp(a,b,λ) {
return a + λ*(b-a);
}
function random(a,b) {
return lerp( a, b, Math.random() );
}
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
$(document).ready( function()
{
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var balls = new Array(12);
for( var i=0; i<balls.length; i++ )
{
while(true)
{
var density = Math.sqrt( random(1,100) );
var r = random(5, 30);
var x = random( r+1, canvas.width-1 -(r+1) ),
y = random( r+1, canvas.height-1 -(r+1) );
var overlap = false;
for( var j=0; j<i; j++ )
{
var _x = balls[j].x - x,
_y = balls[j].y - y,
d2 = _x*_x + _y*_y,
_r = balls[j].r + r;
if( d2 < _r*_r )
overlap = true;
}
if( overlap )
continue;
balls[i] = {
color : d3.hsl( lerp(0,240,density/10), random(.3,.7), random(.3,.7) ).toString(),
x : x,
y : y,
vx : random(0, 0.2),
vy : random(0, 0.2),
r : r,
advance: function(t) {
this.x += t * this.vx;
this.y += t * this.vy;
}
};
break;
}
};
window.requestAnimationFrame(vsync);
var t_last;
function vsync(t)
{
if( t_last )
render( t - t_last );
t_last = t;
window.requestAnimationFrame(vsync);
}
function render(t_frame)
{
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
ctx.fillStyle="gray";
ctx.fillRect(0,0, canvas.width, canvas.height);
function advance_all(t) {
balls.forEach( function(b) {
b.advance(t);
});
}
var t_remaining = t_frame;
while(true) {
var hit = get_next_collision( balls, canvas.width, canvas.height );
if( t_remaining < hit.t )
break;
advance_all( hit.t );
t_remaining -= hit.t;
collide_wall( balls[hit.i], hit.wall );
balls[hit.i].advance(.001);
};
advance_all( t_remaining );
// draw balls
balls.forEach( function(ball) {
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.r, 0, Math.PI*2, true);
ctx.closePath();
ctx.fillStyle = ball.color;
ctx.fill();
});
}
});
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
function get_next_collision(balls,W,H)
{
var winner;
// ball-wall
balls.forEach( function(ball,i)
{
var t = [];
t['L'] = (ball.r - ball.x) / ball.vx; // s.x + t v.x = r
t['T'] = (ball.r - ball.y) / ball.vy;
t['R'] = (W-1-ball.r - ball.x) / ball.vx; // s.x + t v.x = (W-1)-r
t['B'] = (H-1-ball.r - ball.y) / ball.vy;
// get index of smallest positive t
var LR = t['L'] >= 0 ? 'L' : 'R',
TB = t['T'] >= 0 ? 'T' : 'B',
wall = t[LR] < t[TB] ? LR : TB;
if( ! winner || ( t[wall] <= winner.t ) )
winner = {
t : t[wall],
i : i,
wall: wall
};
});
return winner;
}
function collide_wall( A, wall )
{
if( wall == 'L' || wall == 'R' )
A.vx *= -1;
else
A.vy *= -1;
}
html, body {
width: 100%;
height: 100%;
margin: 0px;
}
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/mathjs/1.5.1/math.min.js"></script>
<script src="http://d3js.org/d3.v3.min.js"></script>
<canvas id="myCanvas">
<!-- Insert fallback content here -->
</canvas>
Why is Firefox performing significantly worse than its competitors?
If I take the number of balls to 500 Chrome is still smooth, Firefox is seriously choppy.
If I take the number of balls down to 1 Firefox is still burring it.
Another experiment I did was applying a fixed velocity increment each frame, so that the smoothness of the animation accurately reflects the evenness of the render callback. This showed Firefox to be all over the place. Chrome on the other hand was smooth.
If there is sufficient interest, I can provide a snippet for that also and maybe tidy up the first one so that it offers a slider to modify the ball count.
As far as I can see, (1) Firefox definitely isn't giving us a genuine VSYNC callback, I suspect it is just using a timer, (2) even correcting for that by manually calculating elapsed time for each frame, it burrs the animation, maybe suggesting that it sometimes the callback fires in time to catch the VSYNC and sometimes it misses the boat, (3) there is an additional compositing hit that is disproportionate compared with Chrome.
Is there anything to be done about this?
EDIT: I found this question from four years ago! Poor performance of html5 canvas under firefox -- please don't mark this question as the duplicate unless it is certain that the answer to that question is still the only relevant answer four years later. I've deliberately included the version number in the question, as the question pertains specifically to the current version.
So I have a Flash ActionScript 2 code, which creates a preset amount of enemies, gives enemies stats, and makes them move around randomly. Code:
//Settings
var mapWidth:Number = 550;
var mapHeight:Number = 400;
var enemiesArray:Array = new Array();
var totalEnemies:Number;
var eClip:MovieClip;
//Math functions
function getdistance(x, y, x1, y1)
{
run = x1-x;
rise = y1-y;
return (hyp(run, rise));
}
function hyp(a, b)
{
return (Math.sqrt(a*a+b*b));
}
function resetDirection(mc:MovieClip)
{
mc.roamTime = random(50);
mc.t = mc.roamTime;
mc.roamDistance = random(60)+25;
mc.randomRoamDistanceX = (Math.random()*mc.roamDistance)+mc.xx-(mc.roamDistance/2);
mc.randomRoamDistanceY = (Math.random()*mc.roamDistance)+mc.yy-(mc.roamDistance/2);
mc.newRoamDistance = getdistance(mc._x, mc._y, mc.randomRoamDistanceX, mc.randomRoamDistanceY);
mc.norm = mc.roamSpeed/mc.newRoamDistance;
mc.finalRoamDistanceX = (mc.randomRoamDistanceX-mc.xx)*mc.norm;
mc.finalRoamDistanceY = (mc.randomRoamDistanceY-mc.yy)*mc.norm;
}
//function to move enemies
function moveIt(mc:MovieClip)
{
//reduce roamTime;
mc.t--;
//move enemy to new position
if (getdistance(mc._x, mc._y, mc.randomRoamDistanceX, mc.randomRoamDistanceY)>mc.roamSpeed) {
mc._x += mc.finalRoamDistanceX;
mc._y += mc.finalRoamDistanceY;
}
//rotate enemy
XXXdiff = mc.xx-mc.randomRoamDistanceX;
YYYdiff = -(mc.yy-mc.randomRoamDistanceY);
rrradAngle = Math.atan(YYYdiff/XXXdiff);
if (XXXdiff<0) {
cccorrFactor = 270;
} else {
cccorrFactor = 90;
}
//
mc.ship_mc._rotation = -(rrradAngle*360/(2*Math.PI)+cccorrFactor);
//check if time to reset, based on roamTime
if (mc.t<=0) {
resetDirection(mc);
}
}
//
// Generate Enemies
//
// set and save enemy stats
//
//
// createEnemies(number of enemies you want, movieclip where you want to create the enemies);
//
function createEnemies(amount:Number, targetLocation:MovieClip) {
trace("createEnemies: "+amount);
for (var i = 0; i<amount; i++) {
randomXpos = Math.round(Math.random()*mapWidth);
randomYpos = Math.round(Math.random()*mapHeight);
//add new enemy to map
var newEnemy:MovieClip = targetLocation.attachMovie("enemy1", "enemy1_"+i, targetLocation.getNextHighestDepth());
enemiesArray.push(newEnemy);
//
//set enemy stats
newEnemy.id = i;
newEnemy._x = randomXpos;
newEnemy._y = randomYpos;
//save x and y position
newEnemy.xx = newEnemy._x;
newEnemy.yy = newEnemy._y;
//
newEnemy.roamSpeed = 2
newEnemy.roamTime = random(50);
newEnemy.roamDistance = random(60)+25;
newEnemy.t = 0;
//
newEnemy.myHealth = 10;
newEnemy.myName = "Small Scout";
//
resetDirection(newEnemy);
//target enemy
newEnemy.onPress = function() {
trace("Enemy: "+this.tName+" "+this.id);
target_txt.text = this.myName+": "+this.id+" Health: "+this.myHealth;
};
newEnemy.onEnterFrame = function() {
moveIt(this);
};
}
}
start_btn.onRelease = function() {
if (start_txt.text == "Start") {
//run the create enemies function to start the engine
createEnemies(box_mc.numberOfEnemies.text, map_mc);
//hide start button
start_txt._visible =false;
this._visible = false;
box_mc._visible = false;
}
};
I want program enemies to be grouped (based on fireflies algorithm). My idea is write for loop to define attractiveness, but I don't know how to make my objects move to the most attractiveness. Maybe someone would help me with this problem?
I change this line:
newEnemy.myHealth = 10;
on this
newEnemy.myHealth = Math.round(random(9)+1);
myHealth would be responsible for attractiveness. I try to use code from this site and modificate code to let objects with low attractiveness follow objects with large attractiveness. Also, I want to stop algorith, when they are in the groups.
I wrote a quick experiment with a genetic algorithm. It simply takes a grid of squares and tries to mutate their color to make them all yellow. It fails miserably and I can't seem to figure out why. I've included a link to JSFiddle that demonstrates working code, as well as a copy of the code in its entirety.
http://jsfiddle.net/mankyd/X6x9L/
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
<div class="container">
<h1>The randomly flashing squares <i>should</i> be turning yellow</h1>
<div class="row">
<canvas id="input_canvas" width="100" height="100"></canvas>
<canvas id="output_canvas" width="100" height="100"></canvas>
</div>
<div class="row">
<span id="generation"></span>
<span id="best_fitness"></span>
<span id="avg_fitness"></span>
</div>
</div>
</body>
</html>
Note that the below javascript relies on jquery in a few places.
// A bit of code that draws several squares in a canvas
// and then attempts to use a genetic algorithm to slowly
// make those squares all yellow.
// Knobs that can be tweaked
var mutation_rate = 0.1; // how often should we mutate something
var crossover_rate = 0.6; // how often should we crossover two parents
var fitness_influence = 1; // affects the fitness's influence over mutation
var elitism = 1; // how many of the parent's generation to carry over
var num_offspring = 20; // how many spawn's per generation
var use_rank_selection = true; // false == roulette_selection
// Global variables for easy tracking
var children = []; // current generation
var best_spawn = null; // keeps track of our best so far
var best_fitness = null; // keeps track of our best so far
var generation = 0; // global generation counter
var clear_color = 'rgb(0,0,0)';
// used for output
var $gen_span = $('#generation');
var $best_fit = $('#best_fitness');
var $avg_fit = $('#avg_fitness');
var $input_canvas = $('#input_canvas');
var input_ctx = $input_canvas[0].getContext('2d');
var $output_canvas = $('#output_canvas');
var output_ctx = $output_canvas[0].getContext('2d');
// A spawn represents a genome - a collection of colored
// squares.
var Spawn = function(nodes) {
var _fitness = null; // a cache of our fitness
this.nodes = nodes; // the squares that make up our image
this.fitness = function() {
// fitness is simply a function of how close to yellow we are.
// This is defined through euclidian distance. Smaller fitnesses
// are better.
if (_fitness === null) {
_fitness = 0;
for (var i = 0; i < nodes.length; i++) {
_fitness += Math.pow(-nodes[i].color[0], 2) +
Math.pow(255 - nodes[i].color[1], 2) +
Math.pow(255 - nodes[i].color[2], 2);
}
_fitness /= 255*255*3*nodes.length; // divide by the worst possible distance
}
return _fitness;
};
this.mutate = function() {
// reset our cached fitness to unknown
_fitness = null;
var health = this.fitness() * fitness_influence;
var width = $output_canvas[0].width;
var height = $output_canvas[0].height;
for (var i = 0; i < nodes.length; i++) {
// Sometimes (most times) we don't mutate
if (Math.random() > mutation_rate) {
continue;
}
// Mutate the colors.
for (var j = 0; j < 3; j++) {
// colors can move by up to 32 in either direction
nodes[i].color[j] += 64 * (.5 - Math.random()) * health;
// make sure that our colors stay between 0 and 255
nodes[i].color[j] = Math.max(0, Math.min(255, nodes[i].color[j]));
}
}
};
this.draw = function(ctx) {
// This draw function is a little overly generic in that it supports
// arbitrary polygons.
ctx.save();
ctx.fillStyle = clear_color;
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
for (var i = 0; i < nodes.length; i++) {
ctx.fillStyle = 'rgba(' + Math.floor(nodes[i].color[0]) + ',' + Math.floor(nodes[i].color[1]) + ',' + Math.floor(nodes[i].color[2]) + ',' + nodes[i].color[3] + ')';
ctx.beginPath();
ctx.moveTo(nodes[i].points[0][0], nodes[i].points[0][1]);
for (var j = 1; j < nodes[i].points.length; j++) {
ctx.lineTo(nodes[i].points[j][0], nodes[i].points[j][1]);
}
ctx.fill();
ctx.closePath();
}
ctx.restore();
};
};
Spawn.from_parents = function(parents) {
// Given two parents, mix them together to get another spawn
var nodes = [];
for (var i = 0; i < parents[0].nodes.length; i++) {
if (Math.random() > 0.5) {
nodes.push($.extend({}, parents[0].nodes[i]));
}
else {
nodes.push($.extend({}, parents[1].nodes[i]));
}
}
var s = new Spawn(nodes);
s.mutate();
return s;
};
Spawn.random = function(width, height) {
// Return a complete random spawn.
var nodes = [];
for (var i = 0; i < width * height; i += 10) {
var n = {
color: [Math.random() * 256, Math.random() * 256, Math.random() * 256, 1],
points: [
[i % width, Math.floor(i / width) * 10],
[(i % width) + 10, Math.floor(i / width) * 10],
[(i % width) + 10, Math.floor(i / width + 1) * 10],
[i % width, Math.floor(i / width + 1) * 10],
]
};
nodes.push(n);
}
return new Spawn(nodes);
};
var select_parents = function(gene_pool) {
if (use_rank_selection) {
return rank_selection(gene_pool);
}
return roulette_selection(gene_pool);
};
var roulette_selection = function(gene_pool) {
var mother = null;
var father = null;
gene_pool = gene_pool.slice(0);
var sum_fitness = 0;
var i = 0;
for (i = 0; i < gene_pool.length; i++) {
sum_fitness += gene_pool[i].fitness();
}
var choose = Math.floor(Math.random() * sum_fitness);
for (i = 0; i < gene_pool.length; i++) {
if (choose <= gene_pool[i].fitness()) {
mother = gene_pool[i];
break;
}
choose -= gene_pool[i].fitness();
}
// now remove the mother and repeat for the father
sum_fitness -= mother.fitness();
gene_pool.splice(i, 1);
choose = Math.floor(Math.random() * sum_fitness);
for (i = 0; i < gene_pool.length; i++) {
if (choose <= gene_pool[i].fitness()) {
father = gene_pool[i];
break;
}
choose -= gene_pool[i].fitness();
}
return [mother, father];
};
var rank_selection = function(gene_pool) {
gene_pool = gene_pool.slice(0);
gene_pool.sort(function(a, b) {
return b.fitness() - a.fitness();
});
var choose_one = function() {
var sum_fitness = (gene_pool.length + 1) * (gene_pool.length / 2);
var choose = Math.floor(Math.random() * sum_fitness);
for (var i = 0; i < gene_pool.length; i++) {
// figure out the sume of the records up to this point. if we exceed
// our chosen spot, we've found our spawn.
if ((i + 1) * (i / 2) >= choose) {
return gene_pool.splice(i, 1)[0];
}
}
return gene_pool.pop(); // last element, if for some reason we get here
};
var mother = choose_one();
var father = choose_one();
return [mother, father];
};
var start = function() {
// Initialize our first generation
var width = $output_canvas[0].width;
var height = $output_canvas[0].height;
generation = 0;
children = [];
for (var j = 0; j < num_offspring; j++) {
children.push(Spawn.random(width, height));
}
// sort by fitness so that our best comes first
children.sort(function(a, b) {
return a.fitness() - b.fitness();
});
best_spawn = children[0];
best_fitness = best_spawn.fitness();
best_spawn.draw(output_ctx);
};
var generate = function(spawn_pool) {
// generate a new set of offspring
var offspring = [];
for (var i = 0; i < num_offspring; i++) {
var parents = select_parents(spawn_pool);
// odds of crossover decrease as we get closer
if (Math.random() * best_fitness < crossover_rate) {
var s = Spawn.from_parents(parents);
}
else {
// quick hack to copy our mother, with possible mutation
var s = Spawn.from_parents([parents[0], parents[0]]);
}
offspring.push(s);
}
// select a number of best from the parent pool (elitism)
for (var i = 0; i < elitism; i++) {
offspring.push(spawn_pool[i]);
}
// sort our offspring by fitness (this includes the parents from elitism). Fittest first.
offspring.sort(function(a, b) {
return a.fitness() - b.fitness();
});
// pick off the number that we want
offspring = offspring.slice(0, num_offspring);
best_spawn = offspring[0];
best_fitness = best_spawn.fitness();
best_spawn.draw(output_ctx);
generation++;
return offspring;
};
var average_fitness = function(generation) {
debugger;
var a = 0;
for (var i = 0; i < generation.length; i++) {
a += generation[i].fitness();
}
return a / generation.length;
};
//Draw yellow and then initialize our first generation
input_ctx.fillStyle = 'yellow';
input_ctx.fillRect(0, 0, input_ctx.canvas.width, input_ctx.canvas.height);
start();
// Our loop function. Use setTimeout to prevent things from freezing
var gen = function() {
children = generate(children);
$gen_span.text('Generation: ' + generation);
$best_fit.text('Best Fitness: ' + best_fitness);
$avg_fit.text('Avg. Fitness: ' + average_fitness(children));
if (generation % 100 === 0) {
console.log('Generation', generation);
console.log('Fitness', best_fitness);
}
setTimeout(gen, 1);
};
gen();
I've commented the code to try to make parsing it easy. The basic idea is quite simple:
Select 1 or 2 parents from the current generation
Mix those one or two parents together
Mutate the result slightly and add it to the next generation
Select the best few parents (1 in the example) and add them to the next generation
Sort and slice off N results and use them for the next generation (potentially a mix of parents and offspring)
Rinse and repeat
The output never gets anywhere near yellow. It quickly falls into a steady state of a sort that looks awful. Where have I gone wrong?
Solved it. It was in the "from_parents" method:
if (Math.random() > 0.5) {
nodes.push($.extend({}, parents[0].nodes[i]));
}
else {
nodes.push($.extend({}, parents[1].nodes[i]));
}
The $.extend() was doing a shallow copy. The obvious solution was to either put true as the first argument which causes a deep copy. This, however, is incredibly slow performance-wise. The better solution was to remove the $.extend() from that chunk of code entirely and instead to move it up to the mutate() method, where I call $.extend() only if a node is actually about to be changed. In other words, it becomes a copy-on-write.
Also, the color I put in the fitness function was wrong :P
I wasn't quite sure how to describe my problem in the subject. I have a plane MC and a crate MC. The plane only flies along the y axis from the bottom of the screen to top. Along the way I want it to randomly drop the crate MC. My code is below. The problem is that the crates spontaneously keep spawning and not near the plane.
function movePlane():void
{
var tempY:Number;
var tempX:Number;
var tempCrate:MovieClip;
var tempPlane:MovieClip;
for (var j:int =planes.length-1; j>=0; j--)
{
tempPlane = planes[j];
tempPlane.y += tempPlane.planeSpeed;
tempCrate = new Crate();
tempY = Math.floor(Math.random() * tempPlane.y);
tempX = Math.floor(Math.random() * tempPlane.x);
}
tempCrate.y = tempY;
tempCrate.x = tempX;
addChild(tempCrate);
}
Edited answer:
To make a crate drop on each plane once you can create this behavior by creating a timer on each plane with a random time value. Like this:
function addRandomCreation():void{
var animationTime:Number = 5000; //The time the planes will be animating in ms
for(var i:int = 0; i < planes.length; i++){
var planeTimer:Timer = new Timer(Math.round(animationTime * Math.random()));
planeTimer.addEventListener(TimerEvent.TIMER, timerComplete(i));
planeTimer.start();
}
}
function timerComplete(planeID:int):function{
return function(event:TimerEvent):void{
event.target.stop();
event.target.removeEventListener(event.type, arguments.callee);
var tempCrate:MovieClip = new Crate();
tempY = Math.round(Math.random() * planes[planeID].y);
tempCrate.y = tempY;
tempCrate.x = planes[planeID].x;
addChild(tempCrate);
}
}
Edited answer:
This will create a crate on the same x axis as the plane it's being created by.
function movePlane():void
{
var tempY:Number;
var tempX:Number;
var tempCrate:MovieClip;
var tempPlane:MovieClip;
for (var j:int =planes.length-1; j>=0; j--)
{
tempPlane = planes[j];
tempPlane.y += tempPlane.planeSpeed;
tempCrate = new Crate();
tempY = Math.floor(Math.random() * tempPlane.y);
tempCrate.y = tempY;
tempCrate.x = tempPlane.x;
addChild(tempCrate);
}
}
You have have to use addChild each time you create a new Crate otherwise it will just create a lot of crates which only the last one will be added to the stage. To do this you have to move the addChild into the loop.
function movePlane():void
{
var tempY:Number;
var tempX:Number;
var tempCrate:MovieClip;
var tempPlane:MovieClip;
for (var j:int =planes.length-1; j>=0; j--)
{
tempPlane = planes[j];
tempPlane.y += tempPlane.planeSpeed;
tempCrate = new Crate();
tempY = Math.floor(Math.random() * tempPlane.y);
tempX = Math.floor(Math.random() * tempPlane.x);
tempCrate.y = tempY;
tempCrate.x = tempX;
addChild(tempCrate);
}
}