Skip to content

Instantly share code, notes, and snippets.

@Bolloxim
Created July 22, 2014 19:44
Show Gist options
  • Save Bolloxim/79f10f0206ebbd29e665 to your computer and use it in GitHub Desktop.
Save Bolloxim/79f10f0206ebbd29e665 to your computer and use it in GitHub Desktop.
A Pen by Andi Smithers.
<body>
<canvas id='moon'></canvas>
</body>
// constant options
const focalDepth = 80;
const focalPoint = 256;
// variables
var centreX;
var centreY;
var mouseX;
var mouseY;
var spawnX;
var spawnY;
var frameCount=0;
var phi = Math.PI*0.5;
var theta = 0;
var autoRotate = 0;
var cameraDepth = 0;
// test multiple groups
var parts = [];
var flyby = [];
// handles negative numbers correctly
function modulo2(a, b)
{
return a-b * Math.floor(a/b);
}
function Part()
{
this.init();
}
Part.prototype.init = function()
{
this.pos = {x:0, y:0, z:0}
this.vel = {x:0, y:0, z:0}
}
Part.prototype.move = function()
{
this.pos.x+=this.vel.x;
this.pos.y+=this.vel.y;
this.pos.z+=this.vel.z;
this.vel.x*=1.03;
this.vel.y*=1.03;
this.vel.z*=1.03;
}
// initialization
function init()
{
// setup canvas and context
canvas = document.getElementById('moon');
context = canvas.getContext('2d');
// set canvas to be window dimensions
resize();
// create event listeners
canvas.addEventListener('mousemove', mouseMove);
canvas.addEventListener('click', mouseClick);
window.addEventListener('resize', resize);
mouseX = centreX;
mouseY = centreY;
// initialze variables
for (var i=0; i<4; i++)
{
parts[i] = new Part();
}
for (var i=0; i<100; i++)
{
flyby[i] = {x:Math.random()*16384-8192, y:Math.random()*2048-1024, z:i*100};
}
}
// input functionsf
function mouseMove(event)
{
var rect = canvas.getBoundingClientRect();
mouseX = event.clientX - rect.left,
mouseY = event.clientY - rect.top
if (!autoRotate)
{
phi = Math.atan2((centreX - mouseX) , canvas.width/2) + (Math.PI*0.5);
theta = Math.atan2((centreY - mouseY) , canvas.height);
}
}
function mouseClick()
{
autoRotate^=1;
if (!autoRotate)
{
phi = Math.atan2((centreX - mouseX) , canvas.width/2) + (Math.PI*0.5);
}
parts[0].vel.x = Math.random()*4;
parts[0].vel.y = Math.random()*4;
parts[0].pos.x = 0;
parts[0].pos.y = 0;
}
function resize()
{
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// compute centre of screen
centreX = canvas.width/2;
centreY = canvas.height/2;
}
// rendering functions
function render()
{
context.fillStyle = 'black';
context.clearRect(0, 0, canvas.width, canvas.height);
context.globalAlpha = 1.0;
context.font = '20pt Calibri';
context.fillStyle = 'rgb(255,255,128)';
context.textAlign = "center";
context.fillText('Zylon Fighter', canvas.width/2, 20);
context.font = '10pt Calibri';
context.fillText('(look up star-raiders on wikipedia)', canvas.width/2, 40);
context.font = '13pt Calibri';
context.fillText('mouse button toggles autorotate', canvas.width/2, canvas.height-30);
// autorotate
if (autoRotate)
{
theta=0;
phi+=0.01;
}
for (var i=99; i>=0; --i)
{
var index = Math.floor(modulo2(i + cameraDepth/100, 100));
var depth = modulo2(flyby[index].z - cameraDepth, 10000);
renderZylon(flyby[index].x, flyby[index].y, depth, phi, theta);
}
}
function renderZylon(x1, y1, z1, rotation, elevation)
{
// compute depth and 3D position
var depth = focalPoint / (z1 + focalDepth);
var x = x1 * depth + centreX;
var y = y1 * depth + centreY;
var size = 100 * depth;
if (!autoRotate)
{
rotation = Math.atan2((x - mouseX) , canvas.width/2) + (Math.PI*0.5);
elevation = Math.atan2((y - mouseY) , canvas.height);
}
if (size<=0) return;
context.globalAlpha = 1.0;
if (z1>5000) context.globalAlpha = 1.0 - (z1-5000)/5000;
// rotate the ship
var angle = rotation;
var c = Math.sin(angle+Math.PI*0.5);
var s = Math.sin(angle);
// exploit symetrical rendering and use angle to determine which wing to do first
var itemAngle = c>=0? angle : angle+Math.PI;
var itemEle = c>=0? elevation*-1 : elevation;
if (s<0)
{
renderWing(x, y, size, itemAngle, itemEle);
renderLauncher(x, y, size, itemAngle);
renderCockpit(x, y, size, angle,elevation);
renderFuselage(x, y, size, angle);
// do other side
itemAngle+=Math.PI;
itemEle*=-1;
renderLauncher(x, y, size, itemAngle);
renderWing(x, y, size, itemAngle, itemEle);
}
else
{
renderWing(x, y, size, itemAngle, itemEle);
renderLauncher(x, y, size, itemAngle);
renderFuselage(x, y, size, angle);
renderCockpit(x, y, size, angle, elevation);
// do other side
itemAngle+=Math.PI;
itemEle*=-1;
renderLauncher(x, y, size, itemAngle);
renderWing(x, y, size, itemAngle, itemEle);
}
}
function renderFuselage(x, y, size, angle)
{
context.fillStyle = 'rgb(255,255,255)';
context.beginPath();
context.arc(x, y, size, 2 * Math.PI, false);
context.fill();
context.lineWidth = size/100 * 8;
context.strokeStyle = '#808080';
context.stroke();
}
function renderCockpit(x, y, size, angle, elev)
{
context.fillStyle = '#000000';
context.beginPath();
var c = Math.cos(angle);
var s = Math.sin(angle);
var ce = Math.cos(elev+Math.PI*0.5);
var se = Math.sin(elev+Math.PI*0.5);
// really should be a fully transformed X not a radius hack
drawEllipseByCenter(context, x+ c*size*0.9, y+ce*size*0.9, s*size, se*size);
context.fill();
context.lineWidth = size/100 * 8;
context.strokeStyle = '#404040';
context.stroke();
}
// found a useful ellipse by centre by Steve Tranby.
function drawEllipseByCenter(ctx, cx, cy, w, h)
{
drawEllipse(ctx, cx - w/2.0, cy - h/2.0, w, h);
}
function drawEllipse(ctx, x, y, w, h)
{
var kappa = .5522848,
ox = (w / 2) * kappa, // control point offset horizontal
oy = (h / 2) * kappa, // control point offset vertical
xe = x + w, // x-end
ye = y + h, // y-end
xm = x + w / 2, // x-middle
ym = y + h / 2; // y-middle
ctx.beginPath();
ctx.moveTo(x, ym);
ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
//ctx.closePath(); // not used correctly, see comments (use to close off open path)
ctx.stroke();
}
function renderWing(x, y, size, angle, ele)
{
var inc = Math.PI /3;
context.fillStyle = '#000000';
context.beginPath();
var s = Math.sin(angle+Math.PI*.5);
var c = Math.sin(angle);
var wingRad = size*1.5;
var wingSize = size*1.6;
context.moveTo(x + c*wingRad + (wingSize*Math.cos(ele))*s, y + wingSize*Math.sin(ele));
for (var i = 1; i < 6; i++)
{
context.lineTo(x + c*wingRad + (wingSize*Math.cos(inc*i+ele))*s, y + wingSize*Math.sin(inc*i+ele));
}
context.closePath();
context.fill();
context.lineWidth = size/100 * 8;;
context.strokeStyle = '#FFFFFF';
context.stroke();
context.beginPath();
for (var i = 0; i < 6; i++)
{
context.moveTo(x + c*wingRad , y+0);
context.lineTo(x + c*wingRad + (wingSize*Math.cos(inc*i+ele))*s, y + wingSize*Math.sin(inc*i+ele));
context.lineWidth = size/100 * 8;
context.strokeStyle = '#FFFFFF';
context.stroke();
}
}
function renderLauncher(x, y, size, angle)
{
var inc = Math.PI /3;
context.fillStyle = '#FFFFFF';
context.beginPath();
var s = Math.sin(angle+Math.PI*.5);
var c = Math.sin(angle);
context.fillStyle = 'rgb(255,255,255)';
context.beginPath();
context.arc(x + c*size*1.25, y, size*0.25, 2 * Math.PI, false);
context.fill();
context.lineWidth = size/100 * 8;
context.strokeStyle = '#808080';
context.stroke();
}
// movement functions
var accel = 5;
function update()
{
accel+=0.1;
if (accel>20) accel = 20;
if (!autoRotate)
cameraDepth += accel;
for (var j=0; j<100; j++)
{
radial = ((modulo2(cameraDepth+flyby[j].z),10000) *Math.PI*2) / 12500;
distance = modulo2(cameraDepth+flyby[j].z,10000) - 5000;
flyby[j].x = distance * Math.sin(radial+j*3) - distance*Math.cos(radial+j);
flyby[j].y = distance * Math.sin(radial+j/2) + distance*Math.cos(radial);
}
for (var i=0; i<4; i++)
{
parts[i].move();
}
}
// per frame tick functions
function animate()
{
frameCount++;
// movement update
update();
// render update
render();
// trigger next frame
requestAnimationFrame(animate);
}
// entry point
init();
animate();
body{
background:#000000
}

Zylon Fighter, Fake 3d

using angles we can fake sorting order of our zylon fighter. Using ellipse rendering for the cockpit we can get a very nice 3d effect. Wings are made of simple hexagons with some line rendering. Its modular.

render a few hundred in a bizarre sin/cos pattern field.

step 3. fragment the ship as though destroyed .. fun stuff

A Pen by Andi Smithers on CodePen.

License.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment