The Heart Thing

Having fun with Paper.js

In this blog i will make a fun app using Paper.js for Windows Store .Paper.js can also be used for building games for Windows Store.

What is PaperScript?

PaperScript is the plain old JavaScript that you are used to, with added support of mathematical operators (+ – * / %) for Point and Size objects. PaperScript code is automatically executed in its own scope that without polluting with the global scope still has access to all the global browser objects and functions, such as document or window.

By default, the Paper.js library only exports one object into the global scope: the paper object. It contains all the classes and objects that the library defines. When working with PaperScript, the user does not need to care about this though, because inside PaperScript code, through the use of clever scoping, all of paper’s objects and functions seem global.

Download the Paper.js library from the following link

http://paperjs.org/download/

Screenshot (199)

Save it in your Computer

Now open Up  Visual Studio  here we will be targeting Universal  apps

Screenshot (202)

I name the app HeartThing

Screenshot (203)

As I opened up Universal app template using JavaScript I found it little puzzled because in the project options I found it targeting it towards Windows Store as well as Windows phone.

Here is the Catch, you need to copy the Paper.js library to the JS folder for both Windows Store as well as Windows Phone and as we are using Blank Template that will result in a single page app.

206

The logic for the game or the app  should be written in the  index.html page and should be implemented both for windows store and phone

<!DOCTYPE html>

<html>

<head>

<meta charset=”utf-8″ />

<title>HeartThing.WindowsPhone</title>

<!– WinJS references –>

<!– At runtime, ui-themed.css resolves to ui-themed.light.css or ui-themed.dark.css

based on the user’s theme setting. This is part of the MRT resource loading functionality. –>

<link href=”/css/ui-themed.css” rel=”stylesheet” />

<script src=”//Microsoft.Phone.WinJS.2.1/js/base.js”></script>

<script src=”//Microsoft.Phone.WinJS.2.1/js/ui.js”></script>

<!– HeartThing.Phone references –>

<link href=”/css/default.css” rel=”stylesheet” />

<script src=”/js/default.js”></script>

<script src=”js/paper-full.js”></script>

<script type=”text/paperscript” canvas=”myCanvas”>

var Boid = Base.extend({

initialize: function(position, maxSpeed, maxForce) {

var strength = Math.random() * 0.5;

this.acceleration = new Point();

this.vector = Point.random() * 2 – 1;

this.position = position.clone();

this.radius = 30;

this.maxSpeed = maxSpeed + strength;

this.maxForce = maxForce + strength;

this.amount = strength * 10 + 10;

this.count = 0;

this.createItems();

},

run: function(boids) {

this.lastLoc = this.position.clone();

if (!groupTogether) {

this.flock(boids);

} else {

this.align(boids);

}

this.borders();

this.update();

this.calculateTail();

this.moveHead();

},

calculateTail: function() {

var segments = this.path.segments,

shortSegments = this.shortPath.segments;

var speed = this.vector.length;

var pieceLength = 5 + speed / 3;

var point = this.position;

segments[0].point = shortSegments[0].point = point;

// Chain goes the other way than the movement

var lastVector = -this.vector;

for (var i = 1; i < this.amount; i++) {

var vector = segments[i].point – point;

this.count += speed * 10;

var wave = Math.sin((this.count + i * 3) / 300);

var sway = lastVector.rotate(90).normalize(wave);

point += lastVector.normalize(pieceLength) + sway;

segments[i].point = point;

if (i < 3)

shortSegments[i].point = point;

lastVector = vector;

}

this.path.smooth();

},

createItems: function() {

this.head = new Shape.Ellipse({

center: [0, 0],

size: [13, 8],

fillColor: ‘red’

});

this.path = new Path({

strokeColor: ‘white’,

strokeWidth: 2,

strokeCap: ’round’

});

for (var i = 0; i < this.amount; i++)

this.path.add(new Point());

this.shortPath = new Path({

strokeColor: ‘white’,

strokeWidth: 4,

strokeCap: ’round’

});

for (var i = 0; i < Math.min(3, this.amount); i++)

this.shortPath.add(new Point());

},

moveHead: function() {

this.head.position = this.position;

this.head.rotation = this.vector.angle;

},

// We accumulate a new acceleration each time based on three rules

flock: function(boids) {

var separation = this.separate(boids) * 3;

var alignment = this.align(boids);

var cohesion = this.cohesion(boids);

this.acceleration += separation + alignment + cohesion;

},

update: function() {

// Update velocity

this.vector += this.acceleration;

// Limit speed (vector#limit?)

this.vector.length = Math.min(this.maxSpeed, this.vector.length);

this.position += this.vector;

// Reset acceleration to 0 each cycle

this.acceleration = new Point();

},

seek: function(target) {

this.acceleration += this.steer(target, false);

},

arrive: function(target) {

this.acceleration += this.steer(target, true);

},

borders: function() {

var vector = new Point();

var position = this.position;

var radius = this.radius;

var size = view.size;

if (position.x < -radius) vector.x = size.width + radius;

if (position.y < -radius) vector.y = size.height + radius;

if (position.x > size.width + radius) vector.x = -size.width -radius;

if (position.y > size.height + radius) vector.y = -size.height -radius;

if (!vector.isZero()) {

this.position += vector;

var segments = this.path.segments;

for (var i = 0; i < this.amount; i++) {

segments[i].point += vector;

}

}

},

// A method that calculates a steering vector towards a target

// Takes a second argument, if true, it slows down as it approaches

// the target

steer: function(target, slowdown) {

var steer,

desired = target – this.position;

var distance = desired.length;

// Two options for desired vector magnitude

// (1 — based on distance, 2 — maxSpeed)

if (slowdown && distance < 100) {

// This damping is somewhat arbitrary:

desired.length = this.maxSpeed * (distance / 100);

} else {

desired.length = this.maxSpeed;

}

steer = desired – this.vector;

steer.length = Math.min(this.maxForce, steer.length);

return steer;

},

separate: function(boids) {

var desiredSeperation = 60;

var steer = new Point();

var count = 0;

// For every boid in the system, check if it’s too close

for (var i = 0, l = boids.length; i < l; i++) {

var other = boids[i];

var vector = this.position – other.position;

var distance = vector.length;

if (distance > 0 && distance < desiredSeperation) {

// Calculate vector pointing away from neighbor

steer += vector.normalize(1 / distance);

count++;

}

}

// Average — divide by how many

if (count > 0)

steer /= count;

if (!steer.isZero()) {

// Implement Reynolds: Steering = Desired – Velocity

steer.length = this.maxSpeed;

steer -= this.vector;

steer.length = Math.min(steer.length, this.maxForce);

}

return steer;

},

// Alignment

// For every nearby boid in the system, calculate the average velocity

align: function(boids) {

var neighborDist = 25;

var steer = new Point();

var count = 0;

for (var i = 0, l = boids.length; i < l; i++) {

var other = boids[i];

var distance = this.position.getDistance(other.position);

if (distance > 0 && distance < neighborDist) {

steer += other.vector;

count++;

}

}

if (count > 0)

steer /= count;

if (!steer.isZero()) {

// Implement Reynolds: Steering = Desired – Velocity

steer.length = this.maxSpeed;

steer -= this.vector;

steer.length = Math.min(steer.length, this.maxForce);

}

return steer;

},

// Cohesion

// For the average location (i.e. center) of all nearby boids,

// calculate steering vector towards that location

cohesion: function(boids) {

var neighborDist = 100;

var sum = new Point();

var count = 0;

for (var i = 0, l = boids.length; i < l; i++) {

var other = boids[i];

var distance = this.position.getDistance(other.position);

if (distance > 0 && distance < neighborDist) {

sum += other.position; // Add location

count++;

}

}

if (count > 0) {

sum /= count;

// Steer towards the location

return this.steer(sum, false);

}

return sum;

}

});

var heartPath = new Path(‘M514.69629,624.70313c-7.10205,-27.02441 -17.2373,-52.39453 -30.40576,-76.10059c-13.17383,-23.70703 -38.65137,-60.52246 -76.44434,-110.45801c-27.71631,-36.64355 -44.78174,-59.89355 -51.19189,-69.74414c-10.5376,-16.02979 -18.15527,-30.74951 -22.84717,-44.14893c-4.69727,-13.39893 -7.04297,-26.97021 -7.04297,-40.71289c0,-25.42432 8.47119,-46.72559 25.42383,-63.90381c16.94775,-17.17871 37.90527,-25.76758 62.87354,-25.76758c25.19287,0 47.06885,8.93262 65.62158,26.79834c13.96826,13.28662 25.30615,33.10059 34.01318,59.4375c7.55859,-25.88037 18.20898,-45.57666 31.95215,-59.09424c19.00879,-18.32178 40.99707,-27.48535 65.96484,-27.48535c24.7373,0 45.69531,8.53564 62.87305,25.5957c17.17871,17.06592 25.76855,37.39551 25.76855,60.98389c0,20.61377 -5.04102,42.08691 -15.11719,64.41895c-10.08203,22.33203 -29.54687,51.59521 -58.40723,87.78271c-37.56738,47.41211 -64.93457,86.35352 -82.11328,116.8125c-13.51758,24.0498 -23.82422,49.24902 -30.9209,75.58594z’);

var boids = [];

var groupTogether = false;

// Add the boids:

for (var i = 0; i < 30; i++) {

var position = Point.random() * view.size;

boids.push(new Boid(position, 10, 0.05));

}

function onFrame(event) {

for (var i = 0, l = boids.length; i < l; i++) {

if (groupTogether) {

var length = ((i + event.count / 30) % l) / l * heartPath.length;

var point = heartPath.getPointAt(length);

if (point)

boids[i].arrive(point);

}

boids[i].run(boids);

}

}

// Reposition the heart path whenever the window is resized:

function onResize(event) {

heartPath.fitBounds(view.bounds);

heartPath.scale(0.8);

}

function onMouseDown(event) {

groupTogether = !groupTogether;

}

function onKeyDown(event) {

if (event.key == ‘space’) {

var layer = project.activeLayer;

layer.selected = !layer.selected;

return false;

}

}

</script>

</head>

<body class=”phone”>

<canvas id=”myCanvas” resize></canvas>

</body>

Now as you click on Debug you will find the app running

With  Left Click the Tadpoles will assemble and form  the heart

Screenshot (204)

With Right click the tadpoles will move apart

Screenshot (205)

What you can do with Paper.js

You can extend the logic and make a simple game out of it with scoring involved and say how fast you form the heart so its just an icing in the cake and you can get going with it.

I will be experimenting and will be writing new stuff on Games or something which is funny and share it.I will be updating the work on my GITHUB library so that you can easily fork it.

This app works with touch also

Its because of a Little angel who always think that I can do more stuff  and together we can go beyond and so this is my first of the more attempts to follow. Thanks to all and specially to the Little angel.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s