Actionscript - randomly drop from moving plane MC - random

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);
}
}

Related

Geometry intersection converting from direct geometry to buffergeometry

I am using Three.js. Found a really good Decal library written by Benpurdy. It's very easily modifiable and also used the techniques described here
However, the technique uses Geometry. The project I am on, uses BufferGeometry. I traced the code which does the geometry intersects and can't figure out the conversion from faces and vertices to attributes.
this.createGeometry = function(matrix, mesh) {
var geom = mesh.geometry;
var decalGeometry = new THREE.Geometry();
var projectorInverse = matrix.clone().getInverse(matrix);
var meshInverse = mesh.matrixWorld.clone().getInverse(mesh.matrixWorld);
var faces = [];
for(var i = 0; i < geom.faces.length; i++){
var verts = [geom.faces[i].a, geom.faces[i].b, geom.faces[i].c];
var pts = [];
var valid = false;
for(var v = 0; v < 3; v++) {
var vec = geom.vertices[verts[v]].clone();
vec.applyMatrix4(mesh.matrixWorld);
vec.applyMatrix4(matrix);
if((vec.z > 1) || (vec.z < -1) || (vec.x > 1) || (vec.x < -1) || (vec.y > 1) || (vec.y < -1)) {
} else {
valid = true;
}
pts.push(vec);
}
if(valid) {
var uv = [];
for(var n = 0; n < 3; n++){
uv.push(new THREE.Vector2( (pts[n].x + 1) / 2, (pts[n].y + 1) / 2));
pts[n].applyMatrix4(projectorInverse);
pts[n].applyMatrix4(meshInverse);
decalGeometry.vertices.push( pts[n] );
}
// update UV's
decalGeometry.faceVertexUvs[0].push(uv);
var newFace = geom.faces[i].clone();
newFace.a = decalGeometry.vertices.length - 3;
newFace.b = decalGeometry.vertices.length - 2;
newFace.c = decalGeometry.vertices.length - 1;
decalGeometry.faces.push(newFace);
}
}
return decalGeometry;
}
Appreciate if anyone could shed some light on how to go about pursuing this? Thanks.
I ended up solving the problem by writing another function to compute intersections with buffergeometry. Took me a while trying to understand the original buffer geometry code.
this.createGeometryFromBufferGeometry = function(matrix, mesh) {
var geom = mesh.geometry;
var decalGeometry = new THREE.Geometry();
var projectorInverse = matrix.clone().getInverse(matrix);
var meshInverse = mesh.matrixWorld.clone().getInverse(mesh.matrixWorld);
var faces = [];
for(var i = 0; i < geom.attributes.position.array.length; i+=9){
var pts = [];
var valid = false;
for(var v = 0; v < 9; v+=3) {
var vec = new THREE.Vector3(geom.attributes.position.array[i+v],geom.attributes.position.array[i+v+1],geom.attributes.position.array[i+v+2]);
console.log((i+v) + " " + (i+v+1) + " " + (i+v+2) );
console.log(vec);
vec.applyMatrix4(mesh.matrixWorld);
vec.applyMatrix4(matrix);
if((vec.z > 1) || (vec.z < -1) || (vec.x > 1) || (vec.x < -1) || (vec.y > 1) || (vec.y < -1)) {
} else {
valid = true;
}
pts.push(vec);
}
if(valid) {
var uv = [];
for(var n = 0; n < 3; n++){
uv.push(new THREE.Vector2( (pts[n].x + 1) / 2, (pts[n].y + 1) / 2));
pts[n].applyMatrix4(projectorInverse);
pts[n].applyMatrix4(meshInverse);
decalGeometry.vertices.push( pts[n] );
}
decalGeometry.faceVertexUvs[0].push(uv);
var newFace = new THREE.Face3()
newFace.a = decalGeometry.vertices.length - 3;
newFace.b = decalGeometry.vertices.length - 2;
newFace.c = decalGeometry.vertices.length - 1;
decalGeometry.faces.push(newFace);
}
}
return decalGeometry;
}
BufferGeometry() has a method .fromGeometry(). Populates this BufferGeometry with data from a Geometry object.
var geom = new THREE.BoxGeometry(1,1,1);
var bufGeom = new THREE.BufferGeometry().fromGeometry(geom);
UPD. You can use the other way round.
var bufGeom = new THREE.BoxBufferGeometry(1,1,1);
var geom = new THREE.Geometry().fromBufferGeometry(bufGeom);
Quick and dirty solution is to create geometry from bufferGeometry and after calculating dispose created geometry
this.compute = function()
{
this.geometry = mesh.geometry
if(this.geometry.attributes)
{
this.geometry = new THREE.Geometry().fromBufferGeometry(this.geometry);
this.computeDecal();
this.geometry.dispose();
}
else
{
this.computeDecal();
}
}

Flash AS2 - grouping enemies

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.

Away3D 4.1 performance issues - how to optimise a scene with many identical meshes?

I have created a simple test using Away3D 4.1 (2500 cubes) but performance is a lot lower than i expected - only 10 FPS.
I assume i am making a noob mistake (being a noob and all) so here are relevant pieces of code:
Lighting:
var light1:DirectionalLight = new DirectionalLight();
light1.position = new Vector3D(400, 300, -200);
light1.lookAt(new Vector3D());
light1.color = 0xFFFFFF;
light1.ambient = 0.25;
lightPicker = new StaticLightPicker([light1]);
Creating cubes:
var material:ColorMaterial = new ColorMaterial(0x999999);
material.lightPicker = lightPicker;
material.specular = 0;
var mesh:Mesh = new Mesh(new CubeGeometry(50, 50, 50), material);
for (var i:uint = 0; i < 50; i++)
{
for (var j:uint = 0; j < 50; j++)
{
var cube:Mesh = Mesh(mesh.clone());
cube.x = 100*(i-25);
cube.y = 25;
cube.z = 100*(j-25);
scene.addChild(cube);
}
}
And the camera:
camera = new Camera3D();
camera.position = new Vector3D(0, 1000, -5000);
camera.lookAt(new Vector3D(0, 0, 0));
camera.lens.far = 10000;
Stage3D output in Scout shows that there are many calls between each drawTriangles call and my basic understanding tells me that drawTriangle calls should be 'batched'.
I know that some other frameworks have batch methods but i havent been able to find anything related to Away3D.
Thanks in advance for any help.
It looks like Merge (thanks to Varnius for spotting that) is the recommended way to do it with previous versions, but it doesn't work in 4.1 (see away3d forum thread).
However, user kurono posted a solution in the forum that works (at least for my scenario) so i reproduce it here in case anyone else has the same problem:
var material:ColorMaterial = new ColorMaterial(0x999999);
material.lightPicker = lightPicker;
material.specular = 0;
var mesh:Mesh = new Mesh(new CubeGeometry(50, 50, 50));
var meshes:Vector.<Mesh> = new Vector.<Mesh>();
for (var i:uint = 0; i < 50; i++)
{
for (var j:uint = 0; j < 50; j++)
{
var cube:Mesh = Mesh(mesh.clone());
cube.x = 100*(i-25);
cube.y = 25;
cube.z = 100*(j-25);
meshes.push(cube);
}
}
var bigMesh:Mesh = doMerge(meshes, material);
scene.add(bigMesh);
The magic is in the doMerge() method:
function doMerge(meshes:Vector.<Mesh>, material:MaterialBase):Mesh
{
var isub:ISubGeometry;
var rawVertsAll:Vector.<Number> = new Vector.<Number>();
var rawIndicesAll:Vector.<uint> = new Vector.<uint>();
var rawUVsAll:Vector.<Number> = new Vector.<Number>();
var rawNormalsAll:Vector.<Number> = new Vector.<Number>();
var rawTangentsAll:Vector.<Number> = new Vector.<Number>();
var offset:uint = 0;
var verts:Vector.<Number>;
var normals:Vector.<Number>;
var tangents:Vector.<Number>;
var uvs:Vector.<Number>;
var indices:Vector.<uint>;
var i:uint;
var j:uint;
var k:uint;
for (k = 0; k < meshes.length; k++)
{
var m:Mesh = meshes[k];
isub = m.geometry.subGeometries[0].cloneWithSeperateBuffers();
isub.applyTransformation(m.transform.clone());
verts = new Vector.<Number>();
normals = new Vector.<Number>();
tangents = new Vector.<Number>();
uvs = new Vector.<Number>();
indices = isub.indexData;
for (i = 0; i < isub.numVertices; i++)
{
verts.push(isub.vertexData[i * isub.vertexStride + isub.vertexOffset]);
verts.push(isub.vertexData[i * isub.vertexStride + isub.vertexOffset + 1]);
verts.push(isub.vertexData[i * isub.vertexStride + isub.vertexOffset + 2]);
normals.push(isub.vertexNormalData[i * isub.vertexNormalStride + isub.vertexNormalOffset]);
normals.push(isub.vertexNormalData[i * isub.vertexNormalStride + isub.vertexNormalOffset + 1]);
normals.push(isub.vertexNormalData[i * isub.vertexNormalStride + isub.vertexNormalOffset + 2]);
tangents.push(isub.vertexTangentData[i * isub.vertexTangentStride + isub.vertexTangentOffset]);
tangents.push(isub.vertexTangentData[i * isub.vertexTangentStride + isub.vertexTangentOffset + 1]);
tangents.push(isub.vertexTangentData[i * isub.vertexTangentStride + isub.vertexTangentOffset + 2]);
uvs.push(isub.UVData[i * isub.UVStride + isub.UVOffset]);
uvs.push(isub.UVData[i * isub.UVStride + isub.UVOffset + 1]);
}
for (j = 0; j < indices.length; j++)
{
indices[j] += offset;
}
offset += isub.numVertices;
rawVertsAll = rawVertsAll.concat(verts);
rawNormalsAll = rawNormalsAll.concat(normals);
rawTangentsAll = rawTangentsAll.concat(tangents);
rawUVsAll = rawUVsAll.concat(uvs);
rawIndicesAll = rawIndicesAll.concat(indices);
}
var geometry:Geometry = new Geometry();
var subGeometry:SubGeometry = new SubGeometry();
subGeometry.updateVertexData(rawVertsAll);
subGeometry.updateIndexData(rawIndicesAll);
subGeometry.updateUVData(rawUVsAll);
subGeometry.updateVertexNormalData(rawNormalsAll);
subGeometry.updateVertexTangentData(rawTangentsAll);
geometry.subGeometries.push(subGeometry);
return new Mesh(geometry, material);
}
And voila! 10fps becomes 60fps
Yeah, you should batch your draw calls. I don't have much experience with Away3D but after a quick look through their API reference it seems that away3d.tools.commands.Merge should help you to merge all those cubes into one large batched mesh.
Are you getting 10 fps in the debug player or the release version?
I ran your code and get 20 fps in the debug player but 50+ fps in the release version.
I tried merging and didn't see any improvements. Plus, if you want to access the individual cubes, merging will make that quite complicated :)

Why Is My Genetic Algorithm Terrible (Why Doesn't It Converge)?

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

Making falling objects come at random angles in AS3

I am making a missile defense type of game and am trying to get the missiles to fall at random angles. I also need the bullet image to turn at the angle I am shooting at. I am very unfamiliar with angles in AS3 so I need some help.
Code:
import spark.components.Image;
public var missiles:Array;
public var bullets:Array;
public var playerLife:Number;
public var targetX:Number;
public var targetY:Number;
public function init():void {
startGame();
}
public function onEnterFrame(e:Event):void {
if(Math.random() <.05 ){
//make a new missle
var newMissile:Image = new Image();
//draw to is
newMissile.source = "assets/missileDown.jpg";
//position it
newMissile.x = Math.random() * stage.stageWidth;
//animate it
newMissile.addEventListener(Event.ENTER_FRAME, onMissileEnterFrame);
//add it to missle array
missiles.push(newMissile);
//add it to the screen
gameGroup.addElement(newMissile);
}
}
public function startGame():void {
//makes new arrays
//gets rid of old arrays
missiles = new Array();
bullets = new Array();
//set player life
playerLife = 5;
//show player life
playerHealth.text = String(playerLife);
//add animation and mouse interation
this.addEventListener(Event.ENTER_FRAME, onEnterFrame);
stage.addEventListener(MouseEvent.CLICK, fireWeapon);
//set game over alpha
gameEnd.alpha = 0;
reset.alpha = 0;
//set game start alpha
playerHealth.alpha = 1;
healthLabel.alpha = 1;
}
//updates the missle
public function onMissileEnterFrame(e:Event):void {
//reference to target
var targetMissile:Image = Image(e.currentTarget);
//move missle down
targetMissile.y += 10;
//if missle has gone too far, remove it and player loses life
if(targetMissile.y > stage.stageHeight) {
playerLife --;
removeMissile(targetMissile);
//show player life
playerHealth.text = String(playerLife);
}
//if player is dead, game over
if(playerLife <= 0) {
gameOver();
}
}
//update bullet
public function onBulletEnterFrame(e:Event):void {
//get reference to bullet
var thisBullet:Bullet = Bullet(e.currentTarget);
//animate towards point..
//calculate difference between current position and desired position
var diffX:Number = thisBullet.targX - thisBullet.x;
var diffY:Number = thisBullet.targY - thisBullet.y;
//move 10% of difference closer
thisBullet.x += diffX * .1;
thisBullet.y += diffY * .1;
//chekc for overlap between bullet and missles
for(var i:Number = 0; i < missiles.length; i++) {
//if they do overlap, remove missle
if( thisBullet.hitTestObject(missiles[i]) ) {
removeMissile(missiles[i]);
removeBullet(thisBullet);
break;
}
}
//if we're 'close enough' to the target position, remove bullet
if(Math.abs(diffX) < 10 && Math.abs(diffY) < 10) {
removeBullet(thisBullet);
}
}
//gets rid of a missle
public function removeMissile(targetMissile:Image):void {
//removes the missle from the missiles array
for(var i:Number = missiles.length - 1; i >= 0; i--) {
if(missiles[i] == targetMissile) {
missiles.splice(i,1);
break;
}
}
//don't animate anymore
targetMissile.removeEventListener(Event.ENTER_FRAME, onMissileEnterFrame);
//remove from stage
gameGroup.removeElement(targetMissile);
}
//removes bullet from stage
public function removeBullet(targetBullet:Bullet):void {
//stop animation
targetBullet.removeEventListener(Event.ENTER_FRAME, onBulletEnterFrame);
//remove from stage
gameGroup.removeElement(targetBullet);
}
//shoot a bullet at the mouse position
public function fireWeapon(e:MouseEvent):void {
//make a new bullet
var newBullet:Bullet = new Bullet();
newBullet.addEventListener(Event.ENTER_FRAME, onBulletEnterFrame);
//position near the earth in the center
var halfStage:Number = stage.stageWidth / 2;
newBullet.x = halfStage;
newBullet.y = 500;
//set target
newBullet.targX = stage.mouseX;
newBullet.targY = stage.mouseY;
//add it to the stage
gameGroup.addElement(newBullet);
}
//you lose
public function gameOver():void {
//remove missles
for(var i:Number = 0; i < missiles.length; i++) {
removeMissile(missiles[i]);
}
//stop interaction
stage.removeEventListener(MouseEvent.CLICK, fireWeapon);
//stop animation
this.removeEventListener(Event.ENTER_FRAME, onEnterFrame);
//set game start alpha
playerHealth.alpha = 0;
healthLabel.alpha = 0;
//set game end alpha
gameEnd.alpha = 1;
reset.alpha = 1;
}
]]>
</fx:Script>
onEnterFrame
...
//position it
newMissile.x = Math.random() * stage.stageWidth;
//rotate it
newMissile.rotation = - (Math.random() * 60 - 30);
onMissileEnterFrame
...
//move missle down
//targetMissile.y += 10;
targetMissile.x -= 10 * Math.sin(targetMissile.rotation * Math.PI/180);
targetMissile.y += 10 * Math.cos(targetMissile.rotation * Math.PI/180);
fireWeapon
...
//set target
newBullet.targX = stage.mouseX;
newBullet.targY = stage.mouseY;
newBullet.rotation = - Math.atan((newBullet.x - newBullet.targX) / (newBullet.y - newBullet.targY)) * 180/Math.PI;

Resources