Categories
Reactickles 3

KeyboardWorm and MouseWorm

After talking things through with Wendy yesterday, I moved on from KeyBoardScalingCircleGrid to KeyboardSnake. Wendy pointed out the multicoloured background wasn’t necessary for this version – so I could concentrate on making the interaction work with a single colour for the snake. I decided to change the name to the slightly more friendly KeyboardWorm.

I began by creating a WormSegment object:

function WormSegment(aColour,aRadius){ //WormSegment object
 this.radius = aRadius;
 this.position = createVector(0.5,0.5); //start in centre of screen
 this.colour = aColour;

this.display = function(){
 var translatedX = this.position.x * windowWidth;
 var translatedY = this.position.y * windowHeight;
 fill(this.colour);
 ellipse(translatedX, translatedY, this.radius, this.radius);
 }
}

Then created an array of these segments to make up a worm:

var worm = []; //array of WormSegment objects
var wormLength = 30; //number of segments of the worm

and

function setup() {
 createCanvas(windowWidth,windowHeight); //make a fullscreen canvas, thanks to: http://codepen.io/grayfuse/pen/wKqLGL
 noStroke(); //no outlines, just filled shapes
 colorMode(HSB, 100);// Use HSB with scale of 0-100, see https://p5js.org/reference/#/p5/color
 var segmentColourMaxBrightness = 100;
 var segmentColourMinBrightness = 80;
 var segmentColourBrightnessRatio = (segmentColourMaxBrightness-segmentColourMinBrightness)/wormLength;
 var segmentColour = color(random(100),50,segmentColourMaxBrightness, 100); //random hue, saturation 50%, brightness 100%, alpha 100%
 var segmentMaxRadius = 100;
 var segmentMinRadius = 70;
 var segmentRadiusRatio = (segmentMaxRadius-segmentMinRadius)/wormLength;
 var segmentRadius = segmentMaxRadius;

for (var i=0; i < wormLength; i++) {
 worm.push(new WormSegment(segmentColour, segmentRadius));
 segmentRadius -= segmentRadiusRatio;
 segmentColour = color(hue(segmentColour), saturation(segmentColour), brightness(segmentColour)-segmentColourBrightnessRatio, alpha(segmentColour));
 }
 console.log("The length of the worm is " + worm.length);
}

I changed my draw() function to the following:

function draw() {
 background(255); //white background
 updateWorm();
 drawWorm();
}

Drawing the worm was pretty straight forward, after I realised that I had to draw the segments in reverse order so that the “head” of the worm drew last:

function drawWorm(){
 //draw the first segment of the worm last so that the shading looks correct
 for (var i = (worm.length-1); i > 0; i--) {
 worm[i].display();
 }
}

Most challenging was updating the worm:

function updateWorm(){
 seekWormTowardsKey(key);

//starting at back of the worm, copy the previous worm segments position onto the current segments position
 for (var i = (worm.length-1); i > 0; i--) {
 worm[i].position.x = worm[i-1].position.x;
 worm[i].position.y = worm[i-1].position.y;
 //had to copy both values - not the reference, worm[i].position = worm[i-1].position doesn't work
 }
}

As I noted in the comments – just setting the position as a reference didn’t work – I had to set each position x and y value manually – this was a nasty bug to track down, after two hours of drawing and redrawing my segment shifting code I finally realised my error.

Seeking the worm towards the key was a matter of checking to make sure that the key was valid, then easing towards the virtual key position – code that I had already written for the KeyboardScalingCircleGrid Reactickle.

function seekWormTowardsKey(aKey){
 var lowerCaseKey = key.toLowerCase(); //key is a system variable via https://p5js.org/reference/#/p5/key, toLowerCase via http://www.w3schools.com/jsref/jsref_tolowercase.asp

if(allTheKeys.includes(lowerCaseKey)){
 //if the key is a valid one, then seek the worm towards it
 seekWormTowardsPosition(getCanvasPositionFromKey(lowerCaseKey));
 }
}

function seekWormTowardsPosition(relativeSeekPosition){
 var easing = 0.05;
 //move the head of the worm a bit closer... via https://processing.org/examples/easing.html
 var dx = relativeSeekPosition.x - worm[0].position.x;
 worm[0].position.x += dx * easing;
 var dy = relativeSeekPosition.y - worm[0].position.y;
 worm[0].position.y += dy * easing;
}

You can try the KeyboardWorm online, as well as a MouseWorm version that I made as a bonus, which also works on Android or iOS mobile devices.