See the blog post - http://codepen.io/pouretrebelle/post/hexagons
alt title - "I'll Gen YOUR Art!"
plenty of keyboard shortcuts for changing things, check out the bottom right.
A Pen by Charlotte Dann on CodePen.
See the blog post - http://codepen.io/pouretrebelle/post/hexagons
alt title - "I'll Gen YOUR Art!"
plenty of keyboard shortcuts for changing things, check out the bottom right.
A Pen by Charlotte Dann on CodePen.
ul.keyboard | |
img.keyboard__icon(src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/2361/keyboard.svg") | |
li.keyboard__item | |
| wipe the board | |
span.keyboard__key W | |
li.keyboard__item | |
| kill all agents | |
span.keyboard__key Q | |
li.keyboard__item | |
| toggle agent visibility | |
span.keyboard__key T | |
li.keyboard__item | |
| toggle mouse visibility | |
span.keyboard__key M | |
li.keyboard__item | |
| toggle hexagon visibility | |
span.keyboard__key H | |
li.keyboard__item | |
| toggle grid visibility | |
span.keyboard__key G | |
li.keyboard__item | |
| add a creator | |
span.keyboard__key C | |
li.keyboard__item | |
| add a destroyer | |
span.keyboard__key D | |
li.keyboard__item | |
| pause/unpause agents | |
span.keyboard__key Space | |
li.keyboard__item | |
| draw | |
span.keyboard__key Click |
// Variables | |
//====================================== | |
const hexRadius = 18; // from center to one of the points | |
const hexLineWeight = 1; // thickness of drawing line | |
const hexDoubleLineOffset = 6; // space between double lines | |
const hexMargin = 2; // space around hexagons | |
const drawLines = true; // draw all the lines | |
const drawPoints = false; // also draw lone double active hexagons as circles | |
const zenoSway = 0.2; // hexagon background colour transition scalar | |
let drawHex = true; // draw hexagon background | |
let drawGrid = true; // when false the background is the same colour as inactive hexagons | |
let creatorCount = 1; // start with 1 creator | |
let destroyerCount = 3; // and 3 destroyers | |
let drawAgents = true; // colour in agent hexagons red/white | |
let agentsMoving = true; // animating the agents, toggled with spacebar | |
let drawMouse = true; // colour in mouse target hexagon in white | |
let mouseTargetHex; // updated with grid position of hovered mouse | |
let mouseLastHex; // used for drag drawing | |
let hexHeight, hexWidth, columns, rows, mousePos; | |
let hexagons = []; | |
let agents = []; | |
// Helper Functions | |
//====================================== | |
function drawHexagon(pixelPos) { | |
// draws hexagon with the center pixelPos | |
push(); | |
translate(pixelPos.x, pixelPos.y); | |
beginShape(); | |
for (let i = 0; i < 6; i++) { | |
vertex((hexRadius-hexMargin/2)*cos(i*Math.PI/3), (hexRadius-hexMargin/2)*sin(i*Math.PI/3)); | |
} | |
endShape(CLOSE); | |
pop(); | |
} | |
function getEdgePos(i, offset) { | |
// return position of this edge of the hexagon | |
// if (offset == 1) clockwise from middle edge | |
// if (offset == 0) middle of edge | |
// if (offset == -1) anti-clockwise from middle edge | |
var pos = createVector(offset*hexDoubleLineOffset*0.5, -hexHeight/2); | |
pos.rotate(i*Math.PI/3); | |
return pos; | |
} | |
function wrap6(num) { | |
// -1 => 5 | |
// 0 => 0 | |
// 5 => 5 | |
// 6 => 0 | |
// 7 => 1 | |
return (num+6) % 6; | |
} | |
function mouseOnScreen() { | |
// return boolean based on whether the mouse is inside the screen or not | |
if (mouseX < 0 || mouseX >= width || mouseX === undefined) return false; | |
if (mouseY < 0 || mouseY >= height || mouseY === undefined) return false; | |
return true; | |
} | |
// Setup | |
//====================================== | |
function setup() { | |
// calculate width and height of hexagons | |
hexWidth = hexRadius * 2; | |
hexHeight = Math.sqrt(3)*hexRadius; | |
// set rows and columns to overlap page edge | |
columns = Math.ceil(window.innerWidth / (hexRadius * 3)); | |
rows = Math.ceil(window.innerHeight / (hexHeight / 2)) + 1; | |
mousePos = createVector(0, 0); | |
// set up canvas | |
createCanvas((columns + 1/4) * (hexRadius * 3), (rows + 1) * (hexHeight / 2)); | |
frameRate(60); | |
// initialise 2D array of hexagons | |
for (let x = 0; x < columns; x++) { | |
hexagons.push([]); | |
for (let y = 0; y < rows; y++) { | |
hexagons[x].push(new Hex(x, y)); | |
} | |
} | |
// neighbouring needs to be done after they're all initialised | |
for (let x = 0; x < columns; x++) { | |
for (let y = 0; y < rows; y++) { | |
hexagons[x][y].initialiseNeighbours(x, y); | |
} | |
} | |
// initialise agents | |
for (let i = 0; i < creatorCount + destroyerCount; i++) { | |
let creator = (i < creatorCount) ? true : false; | |
agents.push(new Agent(creator)); | |
} | |
} | |
// Global Draw | |
//====================================== | |
function draw() { | |
if (drawGrid) { | |
background(25); | |
} else { | |
background(9); | |
} | |
// important to draw all hexagons before lines to avoid overlap | |
if (drawHex || drawGrid) { | |
for (let y = 0; y < rows; y++) { | |
for (let x = 0; x < columns; x++) { | |
hexagons[x][y].drawHex(); | |
} | |
} | |
} | |
if (drawAgents) { | |
for (let i = 0; i < creatorCount + destroyerCount; i++) { | |
agents[i].draw(); | |
} | |
} | |
// draw hexagon at mouse position | |
if (drawMouse) drawMouseHexagon(); | |
if (drawLines) { | |
for (let y = 0; y < rows; y++) { | |
for (let x = 0; x < columns; x++) { | |
hexagons[x][y].drawLines(); | |
} | |
} | |
} | |
update(); | |
} | |
// Global Update | |
//====================================== | |
function update() { | |
mousePos.x = mouseX; | |
mousePos.y = mouseY; | |
if (agentsMoving) { | |
for (let i = 0; i < creatorCount + destroyerCount; i++) { | |
agents[i].update(); | |
} | |
} | |
for (let y = 0; y < rows; y++) { | |
for (let x = 0; x < columns; x++) { | |
hexagons[x][y].update(); | |
} | |
} | |
} | |
// Agent Class | |
//====================================== | |
class Agent { | |
constructor(creator) { | |
// randomly place near centre of screen | |
this.x = Math.round(columns * (0.3 + random(0.4))); | |
this.y = Math.round(rows * (0.3 + random(0.4))); | |
// set random direction 0-5 | |
this.dir = Math.floor(random(0, 6)); | |
// set its morality | |
this.creator = creator; | |
} | |
draw() { | |
noStroke(); | |
if (this.creator) { | |
fill(255, 30); | |
} else { | |
fill(255, 0, 100, 40); | |
} | |
// grab pixel position from corresponding hexagon | |
drawHexagon(hexagons[this.x][this.y].pixelPos); | |
} | |
update() { | |
// get current hexagon by x, y | |
var curHex = hexagons[this.x][this.y]; | |
// increment or decrement activity | |
// if creator and not double active | |
if (this.creator) { | |
if (curHex.nextActive < 2) { | |
curHex.nextActive++; | |
} | |
} | |
// if destroyer and active | |
else { | |
if (curHex.nextActive > 0) { | |
curHex.nextActive--; | |
} | |
} | |
// randomly chose direction -1 to 1 | |
this.dir += -1 + Math.floor(random(3)); | |
// make direction wrap around 0-5 | |
this.dir = wrap6(this.dir); | |
// get next hexagon from current's neighbours | |
var nextHex = curHex.neighbours[this.dir]; | |
// if next hexagon doesn't exist turn around | |
if (nextHex === false) { | |
this.dir = wrap6(this.dir + 3); | |
nextHex = curHex.neighbours[this.dir]; | |
// if that doesn't work it's a corner | |
// return and try again next round | |
if (nextHex === false) return; | |
} | |
// update x and y from next hexagon | |
this.x = nextHex.pos.x; | |
this.y = nextHex.pos.y; | |
} | |
} | |
// Hexagon Class | |
//====================================== | |
class Hex { | |
constructor(x, y) { | |
// establish grid position | |
this.pos = createVector(x, y); | |
// establish pixel position | |
this.pixelPos = createVector(0, 0); | |
this.pixelPos.x = hexWidth * (1.5 * x + 0.5 + y % 2 * 0.75); | |
this.pixelPos.y = hexHeight * (y * 0.5 + 0.5); | |
// establish state | |
// active can be 0, 1, 2 | |
// double active results in double lines | |
this.active = 0; | |
this.nextActive = 0; | |
// establish neighbours | |
this.neighbours = []; | |
// chose random layout (1-3) for dense (4/5/6 neighbours) display | |
// regenerated when hex goes from inactive to active | |
this.denseLayout = Math.ceil(random(3)); | |
// lazily updating count of active neighbours | |
// used to colour hexagons | |
this.zenosNeighbours = 0; | |
} | |
initialiseNeighbours(x, y) { | |
// initialise neighbours called after all hexagons are constructed | |
// because otherwise the hexagons array isn't full yet | |
// lots of conditionals to allow for edge hexagons | |
// start with array of falses | |
let n = [false, false, false, false, false, false]; | |
const odd = y%2; | |
// above | |
if (y >= 2) { | |
n[0] = hexagons[x][y-2]; | |
} | |
// top right | |
if (y >= 1) { | |
if (!odd || x < columns-1) { | |
n[1] = hexagons[x+odd][y-1]; | |
} | |
} | |
// bottom right | |
if (y < rows-1) { | |
if (!odd || x < columns-1) { | |
n[2] = hexagons[x+odd][y+1]; | |
} | |
} | |
// bottom | |
if (y < rows-2) { | |
n[3] = hexagons[x][y+2]; | |
} | |
// bottom left | |
if (y < rows-1) { | |
if (odd || x >= 1) { | |
n[4] = hexagons[x-1+odd][y+1]; | |
} | |
} | |
// top left | |
if (y >= 1) { | |
if (odd || x >= 1) { | |
n[5] = hexagons[x-1+odd][y-1]; | |
} | |
} | |
this.neighbours = n; | |
} | |
update() { | |
// increment layout if hex is becoming active | |
if (!this.active && this.nextActive) { | |
this.denseLayout = (this.denseLayout == 3) ? 1 : this.denseLayout+1; | |
} | |
// update active from next active | |
this.active = this.nextActive; | |
// lazily update zenosNeighbours | |
// zenosNeighbours lazily equals the amount of active neighbours (0-6) | |
// plus the currect active state (0-2) | |
if (this.zenosNeighbours == 0) { | |
// make accurate if 0 | |
this.zenosNeighbours = this.countActiveNeighbours() + this.active; | |
} else { | |
this.zenosNeighbours = this.zenosNeighbours*(1-zenoSway) + zenoSway*(this.countActiveNeighbours() + this.active); | |
} | |
// roughly check whether the mouse is inside the hexagon | |
// update mouse target if so | |
if (mousePos.dist(this.pixelPos) < hexRadius) { | |
mouseTargetHex = this; | |
} | |
} | |
countActiveNeighbours() { | |
// returns number of active neighbours | |
let activeNeighbours = 0; | |
for (let i = 0; i < 6; i++) { | |
if (this.neighbours[i] && this.neighbours[i].active) { | |
activeNeighbours++; | |
} | |
} | |
return activeNeighbours; | |
} | |
getActiveNeighbours() { | |
// returns array of booleans for active neighbours | |
let activeNeighbours = []; | |
for (let i = 0; i < 6; i++) { | |
// if neighbour exists and is active | |
if (this.neighbours[i] && this.neighbours[i].active) { | |
activeNeighbours.push(true); | |
} else { | |
activeNeighbours.push(false); | |
} | |
} | |
return activeNeighbours; | |
} | |
drawHex() { | |
// called in global draw | |
noStroke(); | |
// even if drawHex is inactive we need to draw them blank | |
// if drawGrid is active | |
fill(0); | |
let brightness = this.zenosNeighbours; | |
if (drawHex && this.active) { | |
fill(5*brightness, | |
6*Math.pow(brightness, 1.6), | |
16*brightness); | |
} | |
drawHexagon(this.pixelPos); | |
} | |
drawLines() { | |
// called in global draw | |
push(); | |
translate(this.pixelPos.x, this.pixelPos.y); | |
if (this.active) { // truthy | |
let activeNeighboursCount = this.countActiveNeighbours(); | |
let activeNeighbours = this.getActiveNeighbours(); | |
stroke(255); | |
strokeWeight(hexLineWeight); | |
noFill(); | |
// no neighbours | |
if (activeNeighboursCount == 0) { | |
if (drawPoints && this.active == 2) { | |
ellipse(0, 0, hexDoubleLineOffset); | |
} | |
} | |
// one neighbour | |
else if (activeNeighboursCount == 1) { | |
let activeEdge = activeNeighbours.indexOf(true); | |
let activeNeighbour = this.neighbours[activeEdge]; | |
// if it is double active | |
// or the active neighbour is double active | |
if (activeNeighbour.active == 2 || | |
this.active == 2) { | |
// if drawPoints is inactive the neighbour must have > 1 active neighbour | |
// to avoid ellipses on an active edge | |
if (drawPoints || activeNeighbour.countActiveNeighbours() > 1) { | |
// get two edge points | |
var pos1 = getEdgePos(activeEdge, 1); | |
var pos2 = getEdgePos(activeEdge, -1); | |
// get two control points | |
var control1 = createVector(hexDoubleLineOffset*0.5, -hexHeight/2+hexDoubleLineOffset).rotate(activeEdge*Math.PI/3); | |
var control2 = createVector(-hexDoubleLineOffset*0.5, -hexHeight/2+hexDoubleLineOffset).rotate(activeEdge*Math.PI/3); | |
// draw bezier curve for arc cap | |
beginShape(); | |
vertex(pos1.x, pos1.y); | |
bezierVertex(control1.x, control1.y, control2.x, control2.y, pos2.x, pos2.y); | |
endShape(); | |
} | |
} | |
} | |
// two or three neighbours | |
else if (activeNeighboursCount == 2 || activeNeighboursCount == 3) { | |
// link up all the active neighbours | |
for (var i = 0; i < 6; i++) { | |
if (activeNeighbours[i]) { | |
for (var j = i+1; j < 6; j++) { | |
if (activeNeighbours[j]) { | |
this.drawCurveBetweenEdges(i, j); | |
} | |
} | |
} | |
} | |
} | |
// four neighbours | |
else if (activeNeighboursCount == 4) { | |
// get the index of each inactive edge | |
let skipped1 = activeNeighbours.indexOf(false); | |
let skipped2 = activeNeighbours.slice(skipped1+1).indexOf(false) + skipped1 + 1; | |
// make list of active edge positions | |
// making sure to loop from the most clockwise edge to avoid 0/5 problem | |
var positions = []; | |
let skippedClockwise = (skipped1 == 0) ? skipped1 : skipped2; | |
for (let i = skippedClockwise; i < skippedClockwise + 6; i++) { | |
if (wrap6(i) != skipped1 && wrap6(i) != skipped2) { | |
positions.push(wrap6(i)); | |
} | |
} | |
// skips are adjacent | |
if ((skipped2 == wrap6(skipped1+1)) | |
|| (skipped1 == 0 && skipped2 == 5)) { | |
if (this.denseLayout == 3) { | |
// connect edges to adjacent edges, ignore straight line | |
this.drawCurveBetweenEdges(positions[0], positions[1]); | |
this.drawCurveBetweenEdges(positions[1], positions[2]); | |
this.drawCurveBetweenEdges(positions[2], positions[3]); | |
} | |
else if (this.denseLayout == 2) { | |
// cross over curves | |
this.drawCurveBetweenEdges(positions[0], positions[2]); | |
this.drawCurveBetweenEdges(positions[1], positions[3]); | |
} | |
else { | |
// pair edges with adjacent edges | |
this.drawCurveBetweenEdges(positions[0], positions[1]); | |
this.drawCurveBetweenEdges(positions[2], positions[3]); | |
} | |
} | |
// 1 and 3 situation | |
// or 2 and 2 | |
else { | |
if (this.denseLayout == 3) { | |
// connect edges to adjacent edges | |
this.drawCurveBetweenEdges(positions[0], positions[1]); | |
this.drawCurveBetweenEdges(positions[1], positions[2]); | |
this.drawCurveBetweenEdges(positions[2], positions[3]); | |
this.drawCurveBetweenEdges(positions[3], positions[0]); | |
} | |
else if (this.denseLayout == 2) { | |
// pair edges with adjacent edges | |
this.drawCurveBetweenEdges(positions[3], positions[0]); | |
this.drawCurveBetweenEdges(positions[1], positions[2]); | |
} | |
else { | |
// pair edges with opposite adjacent edges | |
this.drawCurveBetweenEdges(positions[0], positions[1]); | |
this.drawCurveBetweenEdges(positions[2], positions[3]); | |
} | |
} | |
} | |
// five neighbours | |
else if (activeNeighboursCount == 5) { | |
let skipped = activeNeighbours.indexOf(false); | |
if (this.denseLayout == 3) { | |
// connect edges to adjacent edges | |
for (var i = skipped; i < 5 + skipped; i++) { | |
var edge1 = (i == skipped) ? i+5 : i; | |
this.drawCurveBetweenEdges(edge1, i+1); | |
} | |
} | |
else if (this.denseLayout == 2) { | |
// batman logo | |
// curve between the two skipped-adjacent edges | |
this.drawCurveBetweenEdges(skipped+1, skipped+5); | |
// connect other 3 to eachother | |
this.drawCurveBetweenEdges(skipped+2, skipped+3); | |
this.drawCurveBetweenEdges(skipped+3, skipped+4); | |
} | |
else if (this.denseLayout == 1) { | |
// evil M | |
// curve the two skipped-adjacent edges to the skipped-opposite edge | |
this.drawCurveBetweenEdges(skipped+1, skipped+3); | |
this.drawCurveBetweenEdges(skipped+5, skipped+3); | |
// curve the other two edges to the skipped-adjacent edges | |
this.drawCurveBetweenEdges(skipped+1, skipped+2); | |
this.drawCurveBetweenEdges(skipped+5, skipped+4); | |
} | |
} | |
// 6 neighbours | |
else { | |
if (this.denseLayout == 3) { | |
// connect edges to adjacent edges | |
for (var i = 0; i < 6; i++) { | |
this.drawCurveBetweenEdges(i, i+1); | |
} | |
} | |
else { | |
// pair edges with adjacent edges | |
// alternate using denseLayout == 2 or 1 | |
for (var i = this.denseLayout - 1; i < 6; i+=2) { | |
this.drawCurveBetweenEdges(i, i+1); | |
} | |
} | |
} | |
} | |
pop(); | |
} | |
drawCurveBetweenEdges(edge1, edge2) { | |
// called by drawLines() | |
// used to determine whether they should be single, double, or diverging | |
// also used to set curve offsets for inner/outer lines | |
// make sure edges are between 0-5 | |
edge1 = wrap6(edge1); | |
edge2 = wrap6(edge2); | |
// should we draw it as a double line? | |
let double = false; | |
// if the tile is double active | |
if (this.active == 2) double = true; | |
// if both of the edge tiles exist and are double active | |
if ((this.neighbours[edge1] && this.neighbours[edge1].active == 2) && | |
(this.neighbours[edge2] && this.neighbours[edge2].active == 2)) { | |
double = true; | |
} | |
if (double) { | |
// set outer control point slightly further than offset width | |
// to create even margin | |
// cos bezier curves, son | |
this.drawCurveWithOffset(edge1, edge2, 1, 1, -hexDoubleLineOffset*0.8); | |
this.drawCurveWithOffset(edge1, edge2, -1, -1, hexDoubleLineOffset*0.5); | |
} | |
// if tile is single active | |
// and not both of the edges are double active | |
else { | |
// if edge1 hexagon exists and is double active | |
if ((this.neighbours[edge1] && this.neighbours[edge1].active == 2)) { | |
// use half offset width for midpoint of diverging lines | |
// set outer control point slightly further to create even margin | |
this.drawCurveWithOffset(edge1, edge2, 1, 0, -hexDoubleLineOffset*0.4); | |
this.drawCurveWithOffset(edge1, edge2, -1, 0, hexDoubleLineOffset*0.25); | |
} | |
// if edge2 hexagon exists and is double active | |
else if ((this.neighbours[edge2] && this.neighbours[edge2].active == 2)) { | |
// use half offset width for midpoint between single and double | |
// set outer control point slightly further to create even margin | |
this.drawCurveWithOffset(edge1, edge2, 0, 1, -hexDoubleLineOffset*0.4); | |
this.drawCurveWithOffset(edge1, edge2, 0, -1, hexDoubleLineOffset*0.25); | |
} | |
// if everything is single | |
else { | |
this.drawCurveWithOffset(edge1, edge2, 0, 0); | |
} | |
} | |
} | |
drawCurveWithOffset(edge1, edge2, offset1, offset2, originOffset) { | |
// called by drawCurveBetweenEdges() | |
// determines which is the inner and outer line | |
// sets offset and draws line accordingly | |
// originOffset is the distance we move the origin point | |
// in the opposite direction of the average angle of the two points | |
let origin = createVector(0, 0); | |
if (originOffset) { | |
origin.y = originOffset; | |
} | |
// set up positions as per offsets | |
let pos1 = getEdgePos(edge1, offset1); | |
let pos2 = getEdgePos(edge2, offset2); | |
// if edge 2 is one clockwise from edge 1 | |
if (edge1 == wrap6(edge2-1)) { | |
// flips offset 2 | |
pos2 = getEdgePos(edge2, -offset2); | |
// brings offset in smooth curve | |
origin.y -= hexRadius * 0.25; | |
origin.rotate((edge1+0.5)*Math.PI/3); | |
} | |
// if edge 2 is one anti-clockwise from edge 1 | |
else if (edge1 == wrap6(edge2+1)) { | |
// flips offset 1 | |
pos1 = getEdgePos(edge1, -offset1); | |
// brings offset in smooth curve | |
origin.y -= hexRadius * 0.25; | |
origin.rotate((edge1-0.5)*Math.PI/3); | |
} | |
// if edge 2 is two clockwise from edge 1 | |
else if (edge1 == wrap6(edge2-2)) { | |
// flips offset 2 | |
pos2 = getEdgePos(edge2, -offset2); | |
origin.rotate((edge1+1)*Math.PI/3); | |
} | |
// if edge 2 is two anti-clockwise from edge 1 | |
else if (edge1 == wrap6(edge2+2)) { | |
// flips offset 1 | |
pos1 = getEdgePos(edge1, -offset1); | |
origin.rotate((edge1-1)*Math.PI/3); | |
} | |
// if edges are opposites | |
// using the line function everything is 1px off | |
// so we just use the quadratic bezier | |
if (Math.abs(edge2-edge1) == 3) { | |
// flip the offset 2 to create parallel lines | |
pos2 = getEdgePos(edge2, -offset2); | |
// reset origin offset so line is straight | |
origin.y = (hexDoubleLineOffset*0.5) * (offset1+offset2)*0.5 * (edge1-edge2)/3; | |
origin.rotate((edge1+1.5)*Math.PI/3); | |
} | |
// draw the line | |
beginShape(); | |
vertex(pos1.x, pos1.y); | |
quadraticVertex(origin.x, origin.y, pos2.x, pos2.y); | |
endShape(); | |
} | |
} | |
// Mouse Events | |
//====================================== | |
function drawMouseHexagon() { | |
fill(255, 50); | |
if (mouseTargetHex && mouseOnScreen()) { | |
drawHexagon(mouseTargetHex.pixelPos); | |
} | |
} | |
function mousePressed() { | |
if (mouseTargetHex && mouseOnScreen()) { | |
// loop between 0 -2 | |
// increment for left mouse button | |
if (mouseButton == LEFT) { | |
mouseTargetHex.nextActive = (mouseTargetHex.nextActive+1)%3; | |
} | |
// decrement for right mouse button | |
else if (mouseButton == RIGHT) { | |
mouseTargetHex.nextActive = (mouseTargetHex.nextActive-1)%3; | |
} | |
// update last mouse hex | |
mouseLastHex = mouseTargetHex; | |
} | |
} | |
function mouseDragged() { | |
// if it exists and is on screen | |
// if it hasn't just been updated (mousePressed) | |
// if it's a different hex from the last updated one | |
if (mouseTargetHex && mouseOnScreen() | |
&& mouseTargetHex.nextActive == mouseTargetHex.active | |
&& mouseLastHex != mouseTargetHex) { | |
// increment for left mouse button | |
if (mouseButton == LEFT && mouseTargetHex.nextActive < 2) { | |
mouseTargetHex.nextActive++; | |
} | |
// decrement for right mouse button | |
else if (mouseButton == RIGHT && mouseTargetHex.nextActive > 0) { | |
mouseTargetHex.nextActive--; | |
} | |
// update last mouse hex | |
mouseLastHex = mouseTargetHex; | |
} | |
} | |
// Keyboard Events | |
//====================================== | |
function keyPressed() { | |
// Spacebar | |
//------------------------------------ | |
if (keyCode == 32) { | |
agentsMoving = !agentsMoving; | |
} | |
// W - wipe the board | |
//------------------------------------ | |
if (keyCode == 87) { | |
for (let x = 0; x < columns; x++) { | |
for (let y = 0; y < rows; y++) { | |
hexagons[x][y].nextActive = false; | |
} | |
} | |
} | |
// Q - kill all agents | |
//------------------------------------ | |
if (keyCode == 81) { | |
creatorCount = 0; | |
destroyerCount = 0; | |
agents = []; | |
} | |
// T - toggle agent visibility | |
//------------------------------------ | |
if (keyCode == 84) { | |
drawAgents = !drawAgents; | |
} | |
// M - toggle mouse visibility | |
//------------------------------------ | |
if (keyCode == 77) { | |
drawMouse = !drawMouse; | |
} | |
// H - toggle hexagon visibility | |
//------------------------------------ | |
if (keyCode == 72) { | |
drawHex = !drawHex; | |
} | |
// G - toggle grid visibility | |
//------------------------------------ | |
if (keyCode == 71) { | |
drawGrid = !drawGrid; | |
} | |
// C - add a creator | |
//------------------------------------ | |
if (keyCode == 67) { | |
creatorCount++; | |
agents.push(new Agent(true)); | |
} | |
// D - add a destroyer | |
//------------------------------------ | |
if (keyCode == 68) { | |
destroyerCount++; | |
agents.push(new Agent(false)); | |
} | |
} |
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.4/p5.min.js"></script> | |
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/2361/helpers.js"></script> |
@import url('https://fonts.googleapis.com/css?family=Ubuntu+Mono'); | |
body { | |
margin: 0; | |
background: rgb(25, 25, 25); | |
overflow: hidden; | |
min-height: 100%; | |
font-family: 'Ubuntu Mono', monospace; | |
} | |
canvas { | |
position: absolute; | |
top: 50%; | |
left: 50%; | |
transform: translateX(-50%) translateY(-50%); | |
user-select: none; | |
&, &:active, &:focus { | |
cursor: crosshair; | |
} | |
} | |
.keyboard { | |
position: absolute; | |
overflow: hidden; | |
list-style: none; | |
z-index: 1; | |
bottom: 0; | |
right: 0; | |
padding: 0.2em 1em 1em; | |
margin: 0; | |
max-height: 2.6em; | |
max-width: 3.6em; | |
text-align: right; | |
font-size: 12px; | |
background-color: rgba(#000, 0); | |
color: #fff; | |
opacity: 0.4; | |
border-top-left-radius: 5px; | |
transition: opacity 0.3s ease, background-color 0.2s ease, max-height 0.3s ease-in-out, max-width 0s 0.3s ease-in-out; | |
&:hover { | |
transition: opacity 0.3s ease, background-color 0.2s ease, max-height 0.3s ease-in-out, max-width 0s 0s ease-in-out; | |
max-height: 21em; | |
max-width: 20em; | |
opacity: 1; | |
background-color: rgba(#000, 0.7); | |
} | |
&__icon { | |
width: 3.4em; | |
height: 3.4em; | |
cursor: pointer; | |
} | |
&__item { | |
margin: 0.5em 0 0; | |
} | |
&__key { | |
display: inline-block; | |
min-width: 3em; | |
text-align: center; | |
background: rgba(#fff, 0.2); | |
border-radius: 2px; | |
padding: 0.2em; | |
margin: -0.2em 0 0 1em; | |
} | |
} |