Creates a new <video> element that contains the audio/video feed
935 | * from a webcam. This can be drawn onto the canvas using video().
936 | * More specific properties of the feed can be passing in a Constraints object.
937 | * See the
938 | * W3C
939 | * spec for possible properties. Note that not all of these are supported
940 | * by all browsers.
941 | * Security note: A new browser security specification requires that getUserMedia,
942 | * which is behind createCapture(), only works when you're running the code locally,
943 | * or on HTTPS. Learn more here
944 | * and here.
945 | *
946 | * @method createCapture
947 | * @param {String|Constant|Object} type type of capture, either VIDEO or
948 | * AUDIO if none specified, default both,
949 | * or a Constraints object
950 | * @param {Function} callback function to be called once
951 | * stream has loaded
952 | * @return {Object|p5.Element} capture video p5.Element
953 | * @example
954 | *
1049 | * var h2 = createElement('h2','im an h2 p5.element!');
1050 | *
1051 | */
1052 | p5.prototype.createElement = function(tag, content) {
1053 | var elt = document.createElement(tag);
1054 | if (typeof content !== 'undefined') {
1055 | elt.innerHTML = content;
1056 | }
1057 | return addElement(elt, this);
1058 | };
1059 |
1060 |
1061 | // =============================================================================
1062 | // p5.Element additions
1063 | // =============================================================================
1064 | /**
1065 | *
1066 | * Adds specified class to the element.
1067 | *
1068 | * @for p5.Element
1069 | * @method addClass
1070 | * @param {String} class name of class to add
1071 | * @return {Object|p5.Element}
1072 | * @example
1073 | *
1074 | * var div = createDiv('div');
1075 | * div.addClass('myClass');
1076 | *
1077 | */
1078 | p5.Element.prototype.addClass = function(c) {
1079 | if (this.elt.className) {
1080 | // PEND don't add class more than once
1081 | //var regex = new RegExp('[^a-zA-Z\d:]?'+c+'[^a-zA-Z\d:]?');
1082 | //if (this.elt.className.search(/[^a-zA-Z\d:]?hi[^a-zA-Z\d:]?/) === -1) {
1083 | this.elt.className = this.elt.className+' '+c;
1084 | //}
1085 | } else {
1086 | this.elt.className = c;
1087 | }
1088 | return this;
1089 | }
1090 |
1091 | /**
1092 | *
1093 | * Removes specified class from the element.
1094 | *
1095 | * @method removeClass
1096 | * @param {String} class name of class to remove
1097 | * @return {Object|p5.Element}
1098 | */
1099 | p5.Element.prototype.removeClass = function(c) {
1100 | var regex = new RegExp('(?:^|\\s)'+c+'(?!\\S)');
1101 | this.elt.className = this.elt.className.replace(regex, '');
1102 | this.elt.className = this.elt.className.replace(/^\s+|\s+$/g, ""); //prettify (optional)
1103 | return this;
1104 | }
1105 |
1106 | /**
1107 | *
1108 | * Attaches the element as a child to the parent specified.
1109 | * Accepts either a string ID, DOM node, or p5.Element.
1110 | * If no argument is specified, an array of children DOM nodes is returned.
1111 | *
1112 | * @method child
1113 | * @param {String|Object|p5.Element} [child] the ID, DOM node, or p5.Element
1114 | * to add to the current element
1115 | * @return {p5.Element}
1116 | * @example
1117 | *
1118 | * var div0 = createDiv('this is the parent');
1119 | * var div1 = createDiv('this is the child');
1120 | * div0.child(div1); // use p5.Element
1121 | *
1122 | *
1123 | * var div0 = createDiv('this is the parent');
1124 | * var div1 = createDiv('this is the child');
1125 | * div1.id('apples');
1126 | * div0.child('apples'); // use id
1127 | *
1128 | *
1129 | * var div0 = createDiv('this is the parent');
1130 | * var elt = document.getElementById('myChildDiv');
1131 | * div0.child(elt); // use element from page
1132 | *
1133 | */
1134 | p5.Element.prototype.child = function(c) {
1135 | if (typeof c === 'undefined'){
1136 | return this.elt.childNodes
1137 | }
1138 | if (typeof c === 'string') {
1139 | if (c[0] === '#') {
1140 | c = c.substring(1);
1141 | }
1142 | c = document.getElementById(c);
1143 | } else if (c instanceof p5.Element) {
1144 | c = c.elt;
1145 | }
1146 | this.elt.appendChild(c);
1147 | return this;
1148 | };
1149 |
1150 | /**
1151 | * Centers a p5 Element either vertically, horizontally,
1152 | * or both, relative to its parent or according to
1153 | * the body if the Element has no parent. If no argument is passed
1154 | * the Element is aligned both vertically and horizontally.
1155 | *
1156 | * @param {String} align passing 'vertical', 'horizontal' aligns element accordingly
1157 | * @return {Object|p5.Element} pointer to p5.Element
1158 | * @example
1159 | *
1160 | * function setup() {
1161 | * var div = createDiv('').size(10,10);
1162 | * div.style('background-color','orange');
1163 | * div.center();
1164 | *
1165 | * }
1166 | *
1167 | */
1168 | p5.Element.prototype.center = function(align) {
1169 | var style = this.elt.style.display;
1170 | var hidden = this.elt.style.display === 'none';
1171 | var parentHidden = this.parent().style.display === 'none';
1172 | var pos = { x : this.elt.offsetLeft, y : this.elt.offsetTop };
1173 |
1174 | if (hidden) this.show();
1175 |
1176 | this.elt.style.display = 'block';
1177 | this.position(0,0);
1178 |
1179 | if (parentHidden) this.parent().style.display = 'block';
1180 |
1181 | var wOffset = Math.abs(this.parent().offsetWidth - this.elt.offsetWidth);
1182 | var hOffset = Math.abs(this.parent().offsetHeight - this.elt.offsetHeight);
1183 | var y = pos.y;
1184 | var x = pos.x;
1185 |
1186 | if (align === 'both' || align === undefined){
1187 | this.position(wOffset/2, hOffset/2);
1188 | }else if (align === 'horizontal'){
1189 | this.position(wOffset/2, y);
1190 | }else if (align === 'vertical'){
1191 | this.position(x, hOffset/2);
1192 | }
1193 |
1194 | this.style('display', style);
1195 |
1196 | if (hidden) this.hide();
1197 |
1198 | if (parentHidden) this.parent().style.display = 'none';
1199 |
1200 | return this;
1201 | };
1202 |
1203 | /**
1204 | *
1205 | * If an argument is given, sets the inner HTML of the element,
1206 | * replacing any existing html. If true is included as a second
1207 | * argument, html is appended instead of replacing existing html.
1208 | * If no arguments are given, returns
1209 | * the inner HTML of the element.
1210 | *
1211 | * @for p5.Element
1212 | * @method html
1213 | * @param {String} [html] the HTML to be placed inside the element
1214 | * @param {boolean} [append] whether to append HTML to existing
1215 | * @return {Object|p5.Element|String}
1216 | * @example
1217 | *
1218 | * var div = createDiv('').size(100,100);
1219 | * div.html('hi');
1220 | *
1221 | *
1222 | * var div = createDiv('Hello ').size(100,100);
1223 | * div.html('World', true);
1224 | *
1225 | */
1226 | p5.Element.prototype.html = function() {
1227 | if (arguments.length === 0) {
1228 | return this.elt.innerHTML;
1229 | } else if (arguments[1]) {
1230 | this.elt.innerHTML += arguments[0];
1231 | return this;
1232 | } else {
1233 | this.elt.innerHTML = arguments[0];
1234 | return this;
1235 | }
1236 | };
1237 |
1238 | /**
1239 | *
1240 | * Sets the position of the element relative to (0, 0) of the
1241 | * window. Essentially, sets position:absolute and left and top
1242 | * properties of style. If no arguments given returns the x and y position
1243 | * of the element in an object.
1244 | *
1245 | * @method position
1246 | * @param {Number} [x] x-position relative to upper left of window
1247 | * @param {Number} [y] y-position relative to upper left of window
1248 | * @return {Object|p5.Element}
1249 | * @example
1250 | *
1251 | * function setup() {
1252 | * var cnv = createCanvas(100, 100);
1253 | * // positions canvas 50px to the right and 100px
1254 | * // below upper left corner of the window
1255 | * cnv.position(50, 100);
1256 | * }
1257 | *
1258 | */
1259 | p5.Element.prototype.position = function() {
1260 | if (arguments.length === 0){
1261 | return { 'x' : this.elt.offsetLeft , 'y' : this.elt.offsetTop };
1262 | }else{
1263 | this.elt.style.position = 'absolute';
1264 | this.elt.style.left = arguments[0]+'px';
1265 | this.elt.style.top = arguments[1]+'px';
1266 | this.x = arguments[0];
1267 | this.y = arguments[1];
1268 | return this;
1269 | }
1270 | };
1271 |
1272 | /* Helper method called by p5.Element.style() */
1273 | p5.Element.prototype._translate = function(){
1274 | this.elt.style.position = 'absolute';
1275 | // save out initial non-translate transform styling
1276 | var transform = '';
1277 | if (this.elt.style.transform) {
1278 | transform = this.elt.style.transform.replace(/translate3d\(.*\)/g, '');
1279 | transform = transform.replace(/translate[X-Z]?\(.*\)/g, '');
1280 | }
1281 | if (arguments.length === 2) {
1282 | this.elt.style.transform = 'translate('+arguments[0]+'px, '+arguments[1]+'px)';
1283 | } else if (arguments.length > 2) {
1284 | this.elt.style.transform = 'translate3d('+arguments[0]+'px,'+arguments[1]+'px,'+arguments[2]+'px)';
1285 | if (arguments.length === 3) {
1286 | this.elt.parentElement.style.perspective = '1000px';
1287 | } else {
1288 | this.elt.parentElement.style.perspective = arguments[3]+'px';
1289 | }
1290 | }
1291 | // add any extra transform styling back on end
1292 | this.elt.style.transform += transform;
1293 | return this;
1294 | };
1295 |
1296 | /* Helper method called by p5.Element.style() */
1297 | p5.Element.prototype._rotate = function(){
1298 | // save out initial non-rotate transform styling
1299 | var transform = '';
1300 | if (this.elt.style.transform) {
1301 | var transform = this.elt.style.transform.replace(/rotate3d\(.*\)/g, '');
1302 | transform = transform.replace(/rotate[X-Z]?\(.*\)/g, '');
1303 | }
1304 |
1305 | if (arguments.length === 1){
1306 | this.elt.style.transform = 'rotate('+arguments[0]+'deg)';
1307 | }else if (arguments.length === 2){
1308 | this.elt.style.transform = 'rotate('+arguments[0]+'deg, '+arguments[1]+'deg)';
1309 | }else if (arguments.length === 3){
1310 | this.elt.style.transform = 'rotateX('+arguments[0]+'deg)';
1311 | this.elt.style.transform += 'rotateY('+arguments[1]+'deg)';
1312 | this.elt.style.transform += 'rotateZ('+arguments[2]+'deg)';
1313 | }
1314 | // add remaining transform back on
1315 | this.elt.style.transform += transform;
1316 | return this;
1317 | };
1318 |
1319 | /**
1320 | * Sets the given style (css) property (1st arg) of the element with the
1321 | * given value (2nd arg). If a single argument is given, .style()
1322 | * returns the value of the given property; however, if the single argument
1323 | * is given in css syntax ('text-align:center'), .style() sets the css
1324 | * appropriatly. .style() also handles 2d and 3d css transforms. If
1325 | * the 1st arg is 'rotate', 'translate', or 'position', the following arguments
1326 | * accept Numbers as values. ('translate', 10, 100, 50);
1327 | *
1328 | * @method style
1329 | * @param {String} property property to be set
1330 | * @param {String|Number|p5.Color} [value] value to assign to property (only String|Number for rotate/translate)
1331 | * @return {String|Object|p5.Element} value of property, if no value is specified
1332 | * or p5.Element
1333 | * @example
1334 | *
1335 | * var myDiv = createDiv("I like pandas.");
1336 | * myDiv.style("font-size", "18px");
1337 | * myDiv.style("color", "#ff0000");
1338 | *
1339 | *
1340 | * var col = color(25,23,200,50);
1341 | * var button = createButton("button");
1342 | * button.style("background-color", col);
1343 | * button.position(10, 10);
1344 | *
1345 | *
1346 | * var myDiv = createDiv("I like lizards.");
1347 | * myDiv.style("position", 20, 20);
1348 | * myDiv.style("rotate", 45);
1349 | *
1350 | *
1351 | * var myDiv;
1352 | * function setup() {
1353 | * background(200);
1354 | * myDiv = createDiv("I like gray.");
1355 | * myDiv.position(20, 20);
1356 | * }
1357 | *
1358 | * function draw() {
1359 | * myDiv.style("font-size", mouseX+"px");
1360 | * }
1361 | *
1362 | */
1363 | p5.Element.prototype.style = function(prop, val) {
1364 | var self = this;
1365 |
1366 | if (val instanceof p5.Color) {
1367 | val = 'rgba(' + val.levels[0] + ',' + val.levels[1] + ',' + val.levels[2] + ',' + val.levels[3]/255 + ')'
1368 | }
1369 |
1370 | if (typeof val === 'undefined') {
1371 | if (prop.indexOf(':') === -1) {
1372 | var styles = window.getComputedStyle(self.elt);
1373 | var style = styles.getPropertyValue(prop);
1374 | return style;
1375 | } else {
1376 | var attrs = prop.split(';');
1377 | for (var i = 0; i < attrs.length; i++) {
1378 | var parts = attrs[i].split(':');
1379 | if (parts[0] && parts[1]) {
1380 | this.elt.style[parts[0].trim()] = parts[1].trim();
1381 | }
1382 | }
1383 | }
1384 | } else {
1385 | if (prop === 'rotate' || prop === 'translate' || prop === 'position'){
1386 | var trans = Array.prototype.shift.apply(arguments);
1387 | var f = this[trans] || this['_'+trans];
1388 | f.apply(this, arguments);
1389 | } else {
1390 | this.elt.style[prop] = val;
1391 | if (prop === 'width' || prop === 'height' || prop === 'left' || prop === 'top') {
1392 | var numVal = val.replace(/\D+/g, '');
1393 | this[prop] = parseInt(numVal, 10); // pend: is this necessary?
1394 | }
1395 | }
1396 | }
1397 | return this;
1398 | };
1399 |
1400 |
1401 | /**
1402 | *
1403 | * Adds a new attribute or changes the value of an existing attribute
1404 | * on the specified element. If no value is specified, returns the
1405 | * value of the given attribute, or null if attribute is not set.
1406 | *
1407 | * @method attribute
1408 | * @param {String} attr attribute to set
1409 | * @param {String} [value] value to assign to attribute
1410 | * @return {String|Object|p5.Element} value of attribute, if no value is
1411 | * specified or p5.Element
1412 | * @example
1413 | *
1414 | * var myDiv = createDiv("I like pandas.");
1415 | * myDiv.attribute("align", "center");
1416 | *
1417 | */
1418 | p5.Element.prototype.attribute = function(attr, value) {
1419 | if (typeof value === 'undefined') {
1420 | return this.elt.getAttribute(attr);
1421 | } else {
1422 | this.elt.setAttribute(attr, value);
1423 | return this;
1424 | }
1425 | };
1426 |
1427 |
1428 | /**
1429 | *
1430 | * Removes an attribute on the specified element.
1431 | *
1432 | * @method removeAttribute
1433 | * @param {String} attr attribute to remove
1434 | * @return {Object|p5.Element}
1435 | *
1436 | * @example
1437 | *
1438 | * var button;
1439 | * var checkbox;
1440 | *
1441 | * function setup() {
1442 | * checkbox = createCheckbox('enable', true);
1443 | * checkbox.changed(enableButton);
1444 | * button = createButton('button');
1445 | * button.position(10, 10);
1446 | * }
1447 | *
1448 | * function enableButton() {
1449 | * if( this.checked() ) {
1450 | * // Re-enable the button
1451 | * button.removeAttribute('disabled');
1452 | * } else {
1453 | * // Disable the button
1454 | * button.attribute('disabled','');
1455 | * }
1456 | * }
1457 | *
1458 | */
1459 | p5.Element.prototype.removeAttribute = function(attr) {
1460 | this.elt.removeAttribute(attr);
1461 | return this;
1462 | };
1463 |
1464 |
1465 | /**
1466 | * Either returns the value of the element if no arguments
1467 | * given, or sets the value of the element.
1468 | *
1469 | * @method value
1470 | * @param {String|Number} [value]
1471 | * @return {String|Object|p5.Element} value of element if no value is specified or p5.Element
1472 | * @example
1473 | *
1474 | * // gets the value
1475 | * var inp;
1476 | * function setup() {
1477 | * inp = createInput('');
1478 | * }
1479 | *
1480 | * function mousePressed() {
1481 | * print(inp.value());
1482 | * }
1483 | *
1484 | *
1485 | * // sets the value
1486 | * var inp;
1487 | * function setup() {
1488 | * inp = createInput('myValue');
1489 | * }
1490 | *
1491 | * function mousePressed() {
1492 | * inp.value("myValue");
1493 | * }
1494 | *
1495 | */
1496 | p5.Element.prototype.value = function() {
1497 | if (arguments.length > 0) {
1498 | this.elt.value = arguments[0];
1499 | return this;
1500 | } else {
1501 | if (this.elt.type === 'range') {
1502 | return parseFloat(this.elt.value);
1503 | }
1504 | else return this.elt.value;
1505 | }
1506 | };
1507 |
1508 | /**
1509 | *
1510 | * Shows the current element. Essentially, setting display:block for the style.
1511 | *
1512 | * @method show
1513 | * @return {Object|p5.Element}
1514 | * @example
1515 | *
1516 | * var div = createDiv('div');
1517 | * div.style("display", "none");
1518 | * div.show(); // turns display to block
1519 | *
1520 | */
1521 | p5.Element.prototype.show = function() {
1522 | this.elt.style.display = 'block';
1523 | return this;
1524 | };
1525 |
1526 | /**
1527 | * Hides the current element. Essentially, setting display:none for the style.
1528 | *
1529 | * @method hide
1530 | * @return {Object|p5.Element}
1531 | * @example
1532 | *
1533 | * var div = createDiv('this is a div');
1534 | * div.hide();
1535 | *
1536 | */
1537 | p5.Element.prototype.hide = function() {
1538 | this.elt.style.display = 'none';
1539 | return this;
1540 | };
1541 |
1542 | /**
1543 | *
1544 | * Sets the width and height of the element. AUTO can be used to
1545 | * only adjust one dimension. If no arguments given returns the width and height
1546 | * of the element in an object.
1547 | *
1548 | * @method size
1549 | * @param {Number} [w] width of the element
1550 | * @param {Number} [h] height of the element
1551 | * @return {Object|p5.Element}
1552 | * @example
1553 | *
1554 | * var div = createDiv('this is a div');
1555 | * div.size(100, 100);
1556 | *
1557 | */
1558 | p5.Element.prototype.size = function(w, h) {
1559 | if (arguments.length === 0){
1560 | return { 'width' : this.elt.offsetWidth , 'height' : this.elt.offsetHeight };
1561 | }else{
1562 | var aW = w;
1563 | var aH = h;
1564 | var AUTO = p5.prototype.AUTO;
1565 | if (aW !== AUTO || aH !== AUTO) {
1566 | if (aW === AUTO) {
1567 | aW = h * this.width / this.height;
1568 | } else if (aH === AUTO) {
1569 | aH = w * this.height / this.width;
1570 | }
1571 | // set diff for cnv vs normal div
1572 | if (this.elt instanceof HTMLCanvasElement) {
1573 | var j = {};
1574 | var k = this.elt.getContext('2d');
1575 | for (var prop in k) {
1576 | j[prop] = k[prop];
1577 | }
1578 | this.elt.setAttribute('width', aW * this._pInst._pixelDensity);
1579 | this.elt.setAttribute('height', aH * this._pInst._pixelDensity);
1580 | this.elt.setAttribute('style', 'width:' + aW + 'px; height:' + aH + 'px');
1581 | this._pInst.scale(this._pInst._pixelDensity, this._pInst._pixelDensity);
1582 | for (var prop in j) {
1583 | this.elt.getContext('2d')[prop] = j[prop];
1584 | }
1585 | } else {
1586 | this.elt.style.width = aW+'px';
1587 | this.elt.style.height = aH+'px';
1588 | this.elt.width = aW;
1589 | this.elt.height = aH;
1590 | this.width = aW;
1591 | this.height = aH;
1592 | }
1593 |
1594 | this.width = this.elt.offsetWidth;
1595 | this.height = this.elt.offsetHeight;
1596 |
1597 | if (this._pInst) { // main canvas associated with p5 instance
1598 | if (this._pInst._curElement.elt === this.elt) {
1599 | this._pInst._setProperty('width', this.elt.offsetWidth);
1600 | this._pInst._setProperty('height', this.elt.offsetHeight);
1601 | }
1602 | }
1603 | }
1604 | return this;
1605 | }
1606 | };
1607 |
1608 | /**
1609 | * Removes the element and deregisters all listeners.
1610 | * @method remove
1611 | * @example
1612 | *
1613 | * var myDiv = createDiv('this is some text');
1614 | * myDiv.remove();
1615 | *
1616 | */
1617 | p5.Element.prototype.remove = function() {
1618 | // deregister events
1619 | for (var ev in this._events) {
1620 | this.elt.removeEventListener(ev, this._events[ev]);
1621 | }
1622 | if (this.elt.parentNode) {
1623 | this.elt.parentNode.removeChild(this.elt);
1624 | }
1625 | delete(this);
1626 | };
1627 |
1628 |
1629 |
1630 | // =============================================================================
1631 | // p5.MediaElement additions
1632 | // =============================================================================
1633 |
1634 |
1635 | /**
1636 | * Extends p5.Element to handle audio and video. In addition to the methods
1637 | * of p5.Element, it also contains methods for controlling media. It is not
1638 | * called directly, but p5.MediaElements are created by calling createVideo,
1639 | * createAudio, and createCapture.
1640 | *
1641 | * @class p5.MediaElement
1642 | * @constructor
1643 | * @param {String} elt DOM node that is wrapped
1644 | * @param {Object} [pInst] pointer to p5 instance
1645 | */
1646 | p5.MediaElement = function(elt, pInst) {
1647 | p5.Element.call(this, elt, pInst);
1648 |
1649 | var self = this;
1650 | this.elt.crossOrigin = 'anonymous';
1651 |
1652 | this._prevTime = 0;
1653 | this._cueIDCounter = 0;
1654 | this._cues = [];
1655 | this._pixelDensity = 1;
1656 |
1657 | /**
1658 | * Path to the media element source.
1659 | *
1660 | * @property src
1661 | * @return {String} src
1662 | */
1663 | Object.defineProperty(self, 'src', {
1664 | get: function() {
1665 | var firstChildSrc = self.elt.children[0].src;
1666 | var srcVal = self.elt.src === window.location.href ? '' : self.elt.src;
1667 | var ret = firstChildSrc === window.location.href ? srcVal : firstChildSrc;
1668 | return ret;
1669 | },
1670 | set: function(newValue) {
1671 | for (var i = 0; i < self.elt.children.length; i++) {
1672 | self.elt.removeChild(self.elt.children[i]);
1673 | }
1674 | var source = document.createElement('source');
1675 | source.src = newValue;
1676 | elt.appendChild(source);
1677 | self.elt.src = newValue;
1678 | },
1679 | });
1680 |
1681 | // private _onended callback, set by the method: onended(callback)
1682 | self._onended = function() {};
1683 | self.elt.onended = function() {
1684 | self._onended(self);
1685 | }
1686 | };
1687 | p5.MediaElement.prototype = Object.create(p5.Element.prototype);
1688 |
1689 |
1690 |
1691 |
1692 | /**
1693 | * Play an HTML5 media element.
1694 | *
1695 | * @method play
1696 | * @return {Object|p5.Element}
1697 | */
1698 | p5.MediaElement.prototype.play = function() {
1699 | if (this.elt.currentTime === this.elt.duration) {
1700 | this.elt.currentTime = 0;
1701 | }
1702 |
1703 | if (this.elt.readyState > 1) {
1704 | this.elt.play();
1705 | } else {
1706 | // in Chrome, playback cannot resume after being stopped and must reload
1707 | this.elt.load();
1708 | this.elt.play();
1709 | }
1710 | return this;
1711 | };
1712 |
1713 | /**
1714 | * Stops an HTML5 media element (sets current time to zero).
1715 | *
1716 | * @method stop
1717 | * @return {Object|p5.Element}
1718 | */
1719 | p5.MediaElement.prototype.stop = function() {
1720 | this.elt.pause();
1721 | this.elt.currentTime = 0;
1722 | return this;
1723 | };
1724 |
1725 | /**
1726 | * Pauses an HTML5 media element.
1727 | *
1728 | * @method pause
1729 | * @return {Object|p5.Element}
1730 | */
1731 | p5.MediaElement.prototype.pause = function() {
1732 | this.elt.pause();
1733 | return this;
1734 | };
1735 |
1736 | /**
1737 | * Set 'loop' to true for an HTML5 media element, and starts playing.
1738 | *
1739 | * @method loop
1740 | * @return {Object|p5.Element}
1741 | */
1742 | p5.MediaElement.prototype.loop = function() {
1743 | this.elt.setAttribute('loop', true);
1744 | this.play();
1745 | return this;
1746 | };
1747 | /**
1748 | * Set 'loop' to false for an HTML5 media element. Element will stop
1749 | * when it reaches the end.
1750 | *
1751 | * @method noLoop
1752 | * @return {Object|p5.Element}
1753 | */
1754 | p5.MediaElement.prototype.noLoop = function() {
1755 | this.elt.setAttribute('loop', false);
1756 | return this;
1757 | };
1758 |
1759 |
1760 | /**
1761 | * Set HTML5 media element to autoplay or not.
1762 | *
1763 | * @method autoplay
1764 | * @param {Boolean} autoplay whether the element should autoplay
1765 | * @return {Object|p5.Element}
1766 | */
1767 | p5.MediaElement.prototype.autoplay = function(val) {
1768 | this.elt.setAttribute('autoplay', val);
1769 | return this;
1770 | };
1771 |
1772 | /**
1773 | * Sets volume for this HTML5 media element. If no argument is given,
1774 | * returns the current volume.
1775 | *
1776 | * @param {Number} [val] volume between 0.0 and 1.0
1777 | * @return {Number|p5.MediaElement} current volume or p5.MediaElement
1778 | * @method volume
1779 | */
1780 | p5.MediaElement.prototype.volume = function(val) {
1781 | if (typeof val === 'undefined') {
1782 | return this.elt.volume;
1783 | } else {
1784 | this.elt.volume = val;
1785 | }
1786 | };
1787 |
1788 | /**
1789 | * If no arguments are given, returns the current playback speed of the
1790 | * element. The speed parameter sets the speed where 2.0 will play the
1791 | * element twice as fast, 0.5 will play at half the speed, and -1 will play
1792 | * the element in normal speed in reverse.(Note that not all browsers support
1793 | * backward playback and even if they do, playback might not be smooth.)
1794 | *
1795 | * @method speed
1796 | * @param {Number} [speed] speed multiplier for element playback
1797 | * @return {Number|Object|p5.MediaElement} current playback speed or p5.MediaElement
1798 | */
1799 | p5.MediaElement.prototype.speed = function(val) {
1800 | if (typeof val === 'undefined') {
1801 | return this.elt.playbackRate;
1802 | } else {
1803 | this.elt.playbackRate = val;
1804 | }
1805 | };
1806 |
1807 | /**
1808 | * If no arguments are given, returns the current time of the element.
1809 | * If an argument is given the current time of the element is set to it.
1810 | *
1811 | * @method time
1812 | * @param {Number} [time] time to jump to (in seconds)
1813 | * @return {Number|Object|p5.MediaElement} current time (in seconds)
1814 | * or p5.MediaElement
1815 | */
1816 | p5.MediaElement.prototype.time = function(val) {
1817 | if (typeof val === 'undefined') {
1818 | return this.elt.currentTime;
1819 | } else {
1820 | this.elt.currentTime = val;
1821 | }
1822 | };
1823 |
1824 | /**
1825 | * Returns the duration of the HTML5 media element.
1826 | *
1827 | * @method duration
1828 | * @return {Number} duration
1829 | */
1830 | p5.MediaElement.prototype.duration = function() {
1831 | return this.elt.duration;
1832 | };
1833 | p5.MediaElement.prototype.pixels = [];
1834 | p5.MediaElement.prototype.loadPixels = function() {
1835 | if (!this.canvas) {
1836 | this.canvas = document.createElement('canvas');
1837 | this.drawingContext = this.canvas.getContext('2d');
1838 | }
1839 | if (this.loadedmetadata) { // wait for metadata for w/h
1840 | if (this.canvas.width !== this.elt.width) {
1841 | this.canvas.width = this.elt.width;
1842 | this.canvas.height = this.elt.height;
1843 | this.width = this.canvas.width;
1844 | this.height = this.canvas.height;
1845 | }
1846 | this.drawingContext.drawImage(this.elt, 0, 0, this.canvas.width, this.canvas.height);
1847 | p5.Renderer2D.prototype.loadPixels.call(this);
1848 | }
1849 | return this;
1850 | }
1851 | p5.MediaElement.prototype.updatePixels = function(x, y, w, h){
1852 | if (this.loadedmetadata) { // wait for metadata
1853 | p5.Renderer2D.prototype.updatePixels.call(this, x, y, w, h);
1854 | }
1855 | return this;
1856 | }
1857 | p5.MediaElement.prototype.get = function(x, y, w, h){
1858 | if (this.loadedmetadata) { // wait for metadata
1859 | return p5.Renderer2D.prototype.get.call(this, x, y, w, h);
1860 | } else if (typeof x === 'undefined') {
1861 | return new p5.Image(1, 1);
1862 | } else if (w > 1) {
1863 | return new p5.Image(x, y, w, h);
1864 | } else {
1865 | return [0, 0, 0, 255];
1866 | }
1867 | };
1868 | p5.MediaElement.prototype.set = function(x, y, imgOrCol){
1869 | if (this.loadedmetadata) { // wait for metadata
1870 | p5.Renderer2D.prototype.set.call(this, x, y, imgOrCol);
1871 | }
1872 | };
1873 | p5.MediaElement.prototype.copy = function(){
1874 | p5.Renderer2D.prototype.copy.apply(this, arguments);
1875 | };
1876 | p5.MediaElement.prototype.mask = function(){
1877 | this.loadPixels();
1878 | p5.Image.prototype.mask.apply(this, arguments);
1879 | };
1880 | /**
1881 | * Schedule an event to be called when the audio or video
1882 | * element reaches the end. If the element is looping,
1883 | * this will not be called. The element is passed in
1884 | * as the argument to the onended callback.
1885 | *
1886 | * @method onended
1887 | * @param {Function} callback function to call when the
1888 | * soundfile has ended. The
1889 | * media element will be passed
1890 | * in as the argument to the
1891 | * callback.
1892 | * @return {Object|p5.MediaElement}
1893 | * @example
1894 | *
1895 | * function setup() {
1896 | * audioEl = createAudio('assets/beat.mp3');
1897 | * audioEl.showControls(true);
1898 | * audioEl.onended(sayDone);
1899 | * }
1900 | *
1901 | * function sayDone(elt) {
1902 | * alert('done playing ' + elt.src );
1903 | * }
1904 | *
1905 | */
1906 | p5.MediaElement.prototype.onended = function(callback) {
1907 | this._onended = callback;
1908 | return this;
1909 | };
1910 |
1911 |
1912 | /*** CONNECT TO WEB AUDIO API / p5.sound.js ***/
1913 |
1914 | /**
1915 | * Send the audio output of this element to a specified audioNode or
1916 | * p5.sound object. If no element is provided, connects to p5's master
1917 | * output. That connection is established when this method is first called.
1918 | * All connections are removed by the .disconnect() method.
1919 | *
1920 | * This method is meant to be used with the p5.sound.js addon library.
1921 | *
1922 | * @method connect
1923 | * @param {AudioNode|p5.sound object} audioNode AudioNode from the Web Audio API,
1924 | * or an object from the p5.sound library
1925 | */
1926 | p5.MediaElement.prototype.connect = function(obj) {
1927 | var audioContext, masterOutput;
1928 |
1929 | // if p5.sound exists, same audio context
1930 | if (typeof p5.prototype.getAudioContext === 'function') {
1931 | audioContext = p5.prototype.getAudioContext();
1932 | masterOutput = p5.soundOut.input;
1933 | } else {
1934 | try {
1935 | audioContext = obj.context;
1936 | masterOutput = audioContext.destination
1937 | } catch(e) {
1938 | throw 'connect() is meant to be used with Web Audio API or p5.sound.js'
1939 | }
1940 | }
1941 |
1942 | // create a Web Audio MediaElementAudioSourceNode if none already exists
1943 | if (!this.audioSourceNode) {
1944 | this.audioSourceNode = audioContext.createMediaElementSource(this.elt);
1945 |
1946 | // connect to master output when this method is first called
1947 | this.audioSourceNode.connect(masterOutput);
1948 | }
1949 |
1950 | // connect to object if provided
1951 | if (obj) {
1952 | if (obj.input) {
1953 | this.audioSourceNode.connect(obj.input);
1954 | } else {
1955 | this.audioSourceNode.connect(obj);
1956 | }
1957 | }
1958 |
1959 | // otherwise connect to master output of p5.sound / AudioContext
1960 | else {
1961 | this.audioSourceNode.connect(masterOutput);
1962 | }
1963 |
1964 | };
1965 |
1966 | /**
1967 | * Disconnect all Web Audio routing, including to master output.
1968 | * This is useful if you want to re-route the output through
1969 | * audio effects, for example.
1970 | *
1971 | * @method disconnect
1972 | */
1973 | p5.MediaElement.prototype.disconnect = function() {
1974 | if (this.audioSourceNode) {
1975 | this.audioSourceNode.disconnect();
1976 | } else {
1977 | throw 'nothing to disconnect';
1978 | }
1979 | };
1980 |
1981 |
1982 | /*** SHOW / HIDE CONTROLS ***/
1983 |
1984 | /**
1985 | * Show the default MediaElement controls, as determined by the web browser.
1986 | *
1987 | * @method showControls
1988 | */
1989 | p5.MediaElement.prototype.showControls = function() {
1990 | // must set style for the element to show on the page
1991 | this.elt.style['text-align'] = 'inherit';
1992 | this.elt.controls = true;
1993 | };
1994 |
1995 | /**
1996 | * Hide the default mediaElement controls.
1997 | *
1998 | * @method hideControls
1999 | */
2000 | p5.MediaElement.prototype.hideControls = function() {
2001 | this.elt.controls = false;
2002 | };
2003 |
2004 | /*** SCHEDULE EVENTS ***/
2005 |
2006 | /**
2007 | * Schedule events to trigger every time a MediaElement
2008 | * (audio/video) reaches a playback cue point.
2009 | *
2010 | * Accepts a callback function, a time (in seconds) at which to trigger
2011 | * the callback, and an optional parameter for the callback.
2012 | *
2013 | * Time will be passed as the first parameter to the callback function,
2014 | * and param will be the second parameter.
2015 | *
2016 | *
2017 | * @method addCue
2018 | * @param {Number} time Time in seconds, relative to this media
2019 | * element's playback. For example, to trigger
2020 | * an event every time playback reaches two
2021 | * seconds, pass in the number 2. This will be
2022 | * passed as the first parameter to
2023 | * the callback function.
2024 | * @param {Function} callback Name of a function that will be
2025 | * called at the given time. The callback will
2026 | * receive time and (optionally) param as its
2027 | * two parameters.
2028 | * @param {Object} [value] An object to be passed as the
2029 | * second parameter to the
2030 | * callback function.
2031 | * @return {Number} id ID of this cue,
2032 | * useful for removeCue(id)
2033 | * @example
2034 | *
2035 | * function setup() {
2036 | * background(255,255,255);
2037 | *
2038 | * audioEl = createAudio('assets/beat.mp3');
2039 | * audioEl.showControls();
2040 | *
2041 | * // schedule three calls to changeBackground
2042 | * audioEl.addCue(0.5, changeBackground, color(255,0,0) );
2043 | * audioEl.addCue(1.0, changeBackground, color(0,255,0) );
2044 | * audioEl.addCue(2.5, changeBackground, color(0,0,255) );
2045 | * audioEl.addCue(3.0, changeBackground, color(0,255,255) );
2046 | * audioEl.addCue(4.2, changeBackground, color(255,255,0) );
2047 | * audioEl.addCue(5.0, changeBackground, color(255,255,0) );
2048 | * }
2049 | *
2050 | * function changeBackground(val) {
2051 | * background(val);
2052 | * }
2053 | *
2054 | */
2055 | p5.MediaElement.prototype.addCue = function(time, callback, val) {
2056 | var id = this._cueIDCounter++;
2057 |
2058 | var cue = new Cue(callback, time, id, val);
2059 | this._cues.push(cue);
2060 |
2061 | if (!this.elt.ontimeupdate) {
2062 | this.elt.ontimeupdate = this._onTimeUpdate.bind(this);
2063 | }
2064 |
2065 | return id;
2066 | };
2067 |
2068 | /**
2069 | * Remove a callback based on its ID. The ID is returned by the
2070 | * addCue method.
2071 | *
2072 | * @method removeCue
2073 | * @param {Number} id ID of the cue, as returned by addCue
2074 | */
2075 | p5.MediaElement.prototype.removeCue = function(id) {
2076 | for (var i = 0; i < this._cues.length; i++) {
2077 | if (this._cues[i] === id) {
2078 | console.log(id)
2079 | this._cues.splice(i, 1);
2080 | }
2081 | }
2082 |
2083 | if (this._cues.length === 0) {
2084 | this.elt.ontimeupdate = null
2085 | }
2086 | };
2087 |
2088 | /**
2089 | * Remove all of the callbacks that had originally been scheduled
2090 | * via the addCue method.
2091 | *
2092 | * @method clearCues
2093 | */
2094 | p5.MediaElement.prototype.clearCues = function() {
2095 | this._cues = [];
2096 | this.elt.ontimeupdate = null;
2097 | };
2098 |
2099 | // private method that checks for cues to be fired if events
2100 | // have been scheduled using addCue(callback, time).
2101 | p5.MediaElement.prototype._onTimeUpdate = function() {
2102 | var playbackTime = this.time();
2103 |
2104 | for (var i = 0 ; i < this._cues.length; i++) {
2105 | var callbackTime = this._cues[i].time;
2106 | var val = this._cues[i].val;
2107 |
2108 |
2109 | if (this._prevTime < callbackTime && callbackTime <= playbackTime) {
2110 |
2111 | // pass the scheduled callbackTime as parameter to the callback
2112 | this._cues[i].callback(val);
2113 | }
2114 |
2115 | }
2116 |
2117 | this._prevTime = playbackTime;
2118 | };
2119 |
2120 |
2121 | // Cue inspired by JavaScript setTimeout, and the
2122 | // Tone.js Transport Timeline Event, MIT License Yotam Mann 2015 tonejs.org
2123 | var Cue = function(callback, time, id, val) {
2124 | this.callback = callback;
2125 | this.time = time;
2126 | this.id = id;
2127 | this.val = val;
2128 | };
2129 |
2130 | // =============================================================================
2131 | // p5.File
2132 | // =============================================================================
2133 |
2134 |
2135 | /**
2136 | * Base class for a file
2137 | * Using this for createFileInput
2138 | *
2139 | * @class p5.File
2140 | * @constructor
2141 | * @param {File} file File that is wrapped
2142 | * @param {Object} [pInst] pointer to p5 instance
2143 | */
2144 | p5.File = function(file, pInst) {
2145 | /**
2146 | * Underlying File object. All normal File methods can be called on this.
2147 | *
2148 | * @property file
2149 | */
2150 | this.file = file;
2151 |
2152 | this._pInst = pInst;
2153 |
2154 | // Splitting out the file type into two components
2155 | // This makes determining if image or text etc simpler
2156 | var typeList = file.type.split('/');
2157 | /**
2158 | * File type (image, text, etc.)
2159 | *
2160 | * @property type
2161 | */
2162 | this.type = typeList[0];
2163 | /**
2164 | * File subtype (usually the file extension jpg, png, xml, etc.)
2165 | *
2166 | * @property subtype
2167 | */
2168 | this.subtype = typeList[1];
2169 | /**
2170 | * File name
2171 | *
2172 | * @property name
2173 | */
2174 | this.name = file.name;
2175 | /**
2176 | * File size
2177 | *
2178 | * @property size
2179 | */
2180 | this.size = file.size;
2181 |
2182 | /**
2183 | * URL string containing image data.
2184 | *
2185 | * @property data
2186 | */
2187 | this.data = undefined;
2188 | };
2189 |
2190 | }));
2191 |
--------------------------------------------------------------------------------
/flappyBird/player.js:
--------------------------------------------------------------------------------
1 | class Player {
2 |
3 | constructor() {
4 | this.x = canvas.width / 3;
5 | this.y = canvas.height / 2;
6 | this.velY = 0;
7 | this.velX = panSpeed;
8 | this.size = 40;
9 | this.dead = false;
10 | this.isOnGround = false;
11 | this.deadOnGroundCount = 0;
12 | this.fallRotation = -PI / 6;
13 | this.pipeRandomNo = 0;
14 | this.pipes1 = new PipePair(true);
15 | this.pipes2 = new PipePair(false, this.pipes1, this.pipeRandomNo);
16 | this.pipes2.setX(1.5 * canvas.width + this.pipes2.topPipe.width / 2);
17 | this.pipeRandomNo++;
18 | this.ground = new Ground();
19 |
20 |
21 | //-----------------------------------------------------------------------
22 | //neat stuff
23 | this.fitness = 0;
24 | this.vision = []; //the input array fed into the neuralNet
25 | this.decision = []; //the out put of the NN
26 | this.unadjustedFitness;
27 | this.lifespan = 0; //how long the player lived for this.fitness
28 | this.bestScore = 0; //stores the this.score achieved used for replay
29 | this.dead = false;
30 | this.score = 0;
31 | this.gen = 0;
32 |
33 | this.genomeInputs = 4;
34 | this.genomeOutputs = 1;
35 | this.brain = new Genome(this.genomeInputs, this.genomeOutputs);
36 | }
37 |
38 |
39 | show() {
40 |
41 | this.pipes1.show();
42 | this.pipes2.show();
43 | push();
44 | translate(this.x - this.size / 2 - 8 + birdSprite.width / 2, this.y - this.size / 2 + birdSprite.height / 2);
45 | if (this.velY < 15) {
46 | rotate(-PI / 6);
47 | this.fallRotation = -PI / 6;
48 | } else if (this.velY <= 25) {
49 | this.fallRotation += PI / 8.0;
50 | this.fallRotation = constrain(this.fallRotation, -PI / 6, PI / 2);
51 | rotate(this.fallRotation);
52 | // rotate(map(this.velY, 10, 25, -PI / 6, PI / 2));
53 | } else {
54 | rotate(PI / 2);
55 | }
56 | image(birdSprite, -birdSprite.width / 2, -birdSprite.height / 2);
57 | pop();
58 |
59 | this.ground.show();
60 | }
61 |
62 | move() {
63 | this.velY += gravity;
64 | if (!this.dead) {
65 | this.velY = constrain(this.velY, -1000, 25);
66 | } else {
67 | this.velY = constrain(this.velY, -1000, 40);
68 | }
69 | if (!this.isOnGround) {
70 | this.y += this.velY;
71 | }
72 |
73 | }
74 |
75 | updatePipes() {
76 | this.pipes1.update();
77 | this.pipes2.update();
78 | this.ground.update();
79 | //if either pipe is off the screen then reset the pipe
80 | if (this.pipes1.offScreen()) {
81 | this.pipes1 = new PipePair(false, this.pipes2, this.pipeRandomNo);
82 | this.pipeRandomNo++;
83 | }
84 | if (this.pipes2.offScreen()) {
85 | this.pipes2 = new PipePair(false, this.pipes1, this.pipeRandomNo);
86 | this.pipeRandomNo++;
87 | }
88 | }
89 |
90 | update() {
91 | this.lifespan++;
92 | this.updatePipes();
93 | this.move();
94 |
95 | if (this.pipes1.playerPassed(this.x - this.size / 2) || this.pipes2.playerPassed(this.x - this.size / 2)) {
96 | this.score++;
97 | }
98 |
99 | if (this.isOnGround) {
100 | this.deadOnGroundCount++;
101 | if (this.deadOnGroundCount > 50) {
102 | // setup();
103 | }
104 | }
105 | if (!dieOff) {
106 | this.checkCollisions();
107 | }
108 | }
109 |
110 | checkCollisions() {
111 | if (!this.dead) {
112 | pauseBecauseDead = false;
113 | }
114 | if (this.pipes1.colided(this)) {
115 | this.dead = true;
116 | pauseBecauseDead = true;
117 | }
118 | if (this.pipes2.colided(this)) {
119 | pauseBecauseDead = true;
120 | this.dead = true;
121 | }
122 |
123 | if (this.ground.collided(this)) {
124 | this.dead = true;
125 | this.isOnGround = true;
126 | pauseBecauseDead = true;
127 | }
128 | if (this.dead && this.velY < 0) {
129 | this.velY = 0;
130 | }
131 |
132 |
133 | }
134 |
135 | flap() {
136 | if (!this.dead && !this.isOnGround) {
137 | // this.velY = -35;
138 | this.velY = -25;
139 | }
140 | }
141 |
142 |
143 |
144 | //-------------------------------------------------------------------neat functions
145 | look() {
146 | //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 0)) {
151 | closestPipe = this.pipes2;
152 | }
153 | var distanceToClosestPipe = closestPipe.bottomPipe.x - this.x;
154 | this.vision[1] = map(distanceToClosestPipe, 0, canvas.width - this.x, 1, 0);
155 | this.vision[2] = map(max(0, closestPipe.bottomPipe.topY - this.y), 0, 700, 0, 1); //height above bottomY
156 | this.vision[3] = map(max(0, this.y - closestPipe.topPipe.bottomY), 0, 700, 0, 1); //distance below topThing
157 |
158 | }
159 |
160 |
161 | //---------------------------------------------------------------------------------------------------------------------------------------------------------
162 | //gets the output of the this.brain then converts them to actions
163 | think() {
164 |
165 | var max = 0;
166 | var maxIndex = 0;
167 | //get the output of the neural network
168 | this.decision = this.brain.feedForward(this.vision);
169 |
170 | if (this.decision[0] > 0.6) {
171 | this.flap();
172 | }
173 | // for (var i = 0; i < this.decision.length; i++) {
174 | // if (this.decision[i] > max) {
175 | // max = this.decision[i];
176 | // maxIndex = i;
177 | // }
178 | // }
179 |
180 |
181 |
182 | //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<= population.genPlayers.length) { //if at the end then return to the start and stop doing it
99 | upToGen = 0;
100 | showBestEachGen = false;
101 | } else { //if not at the end then get the next generation
102 | genPlayerTemp = population.genPlayers[upToGen].cloneForReplay();
103 | }
104 | }
105 | }
106 | //-----------------------------------------------------------------------------------
107 | function showHumanPlaying() {
108 | if (!humanPlayer.dead) { //if the player isnt dead then move and show the player based on input
109 | humanPlayer.look();
110 | humanPlayer.update();
111 | humanPlayer.show();
112 | } else { //once done return to ai
113 | humanPlaying = false;
114 | }
115 | }
116 | //-----------------------------------------------------------------------------------
117 | function showBestEverPlayer() {
118 | if (!population.bestPlayer.dead) { //if best player is not dead
119 | population.bestPlayer.look();
120 | population.bestPlayer.think();
121 | population.bestPlayer.update();
122 | population.bestPlayer.show();
123 | } else { //once dead
124 | runBest = false; //stop replaying it
125 | population.bestPlayer = population.bestPlayer.cloneForReplay(); //reset the best player so it can play again
126 | }
127 | }
128 | //---------------------------------------------------------------------------------------------------------------------------------------------------------
129 | //draws the display screen
130 | function drawToScreen() {
131 | if (!showNothing) {
132 | //pretty stuff
133 | image(backgroundSprite, 0, 0, canvas.width, canvas.height);
134 | // showAll();
135 | // updateAll();
136 | drawBrain();
137 |
138 |
139 | }
140 | }
141 | //-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
142 | function drawBrain() { //show the brain of whatever genome is currently showing
143 | var startX = 350; //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 10) {
220 | speed -= 10;
221 | frameRate(speed);
222 | print(speed);
223 | }
224 | break;
225 | case 'B': //run the best
226 | runBest = !runBest;
227 | break;
228 | case 'G': //show generations
229 | showBestEachGen = !showBestEachGen;
230 | upToGen = 0;
231 | genPlayerTemp = population.genPlayers[upToGen].clone();
232 | break;
233 | case 'N': //show absolutely nothing in order to speed up computation
234 | showNothing = !showNothing;
235 | break;
236 | case 'P': //play
237 | humanPlaying = !humanPlaying;
238 | humanPlayer = new Player();
239 | break;
240 | }
241 | //any of the arrow keys
242 | switch (keyCode) {
243 |
244 | case RIGHT_ARROW: //right is used to move through the generations
245 |
246 | if (showBestEachGen) { //if showing the best player each generation then move on to the next generation
247 | upToGen++;
248 | if (upToGen >= population.genPlayers.length) { //if reached the current generation then exit out of the showing generations mode
249 | showBestEachGen = false;
250 | } else {
251 | genPlayerTemp = population.genPlayers[upToGen].cloneForReplay();
252 | }
253 | }
254 | break;
255 | }
256 | }
257 |
--------------------------------------------------------------------------------