Skip to content

HatAndBread/s2pd

master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
 
 
 
 
src
 
 
 
 
 
 
 
 
 
 
 
 
 
 

s2pd

Hi! πŸ‘‹πŸŒˆ

s2pd is a stupidly simple HTML5 canvas and web audio library for making 2D games and art in JavaScript.

I originally created s2pd as a library for programming my own simple games and canvas animations, but it quickly got out of hand and took on a life of its own as a fully functioning (although bare-bones) game / html5 canvas library. It is my hope that s2pd is easy and intuitive enough to be used by beginners, students, and anyone else who is curious about delving into the world of digital art. Although there are a vast number of great JavaScript game and canvas libraries out there, I hope s2pd can find its humble place among them as a slightly dumber sibling. Bug reports and code contributions are very welcome!

Here are a few hastily thrown together examples of the kind of things you can build with s2pd. Enjoy!

Example Source
πŸ‘ΎSpace Invaders(ish) cloneπŸ‘Ύ Source code
πŸ‘ΎFlappy Birds(ish) cloneπŸ‘Ύ Source code
πŸ‘ΎVery hastily built platform exampleπŸ‘Ύ Source code

Contents

Installation

The easiest way to get started is to play with s2pd on CodeSandbox. The next easiest way to start is to download boiler-plate.zip and start editing main.js right away. You will need to have a development server running to open index.html.

Otherwise...

With WebPack

npm install s2pd
import s from 's2pd';

Without WebPack

git clone https://github.com/HatAndBread/s2pd.git

In your JavaScript file.

import s from './s2pd/s2pd.js';

OR

There are also two minified versions of s2pd available.s2pd.js can be imported into your project as an es6 module. Alternatively, s2pd.glob.js can be included in the head section of your html file. It is recommended, however, that you use a non-minified version in development so that you can see code hints in your text editor.

<script src="s2pd.glob.js" defer></script>
<script src="my-game.js" defer></script>

Quick tutorial

Let's make a stupidly simple game! First, let's create a canvas.

import s from './s2pd/s2pd.js';
s.ezSetup(); // Basic setup for games. For more advanced options see API.

Now we have an empty canvas element. Let's give it a background using this image file:



The Background class will automatically create an infinitely repeating tile background taking up the entire width and height of your canvas (and infinitely beyond)!

const clouds = new s.Background('./clouds.png');

Now let's make the clouds appear to move. We can use the background's velX property (velocity along the x axis) to make the clouds drift to the left.

clouds.velX = -2; //clouds move -2 pixels along the x axis each call of the animation loop. We can use velY to make things move along the y axis.

If we try to run the program now we will only see a stationary image.😭 To complete the animation we need to create an animation loop! The animation loop will be called roughly 60 times per second.

s.loop(function () {
  // Everything in this function will be called each tick of the loop.
});

All together we have...

import s from './s2pd/s2pd.js';
s.ezSetup();
const clouds = new s.Background('./clouds.png');
clouds.velX = -2;
s.loop(function () {});

Which gives us this

Now let's add a sprite. Making a sprite is simple in s2pd. All you need is an image file with your animation laid out in a single horizontal row with each frame evenly spaced. Let's use this image:

Here we have 35 evenly spaced frames. Perfect! We can make a sprite like this:

const sprite = new s.Sprite(s.width / 2, s.height / 2, './hero.png', 35, 4);
// For a single frame sprite all you need is the first three arguments.

This will create an animated sprite in the center of the canvas. 35 is the number of frames in the image and 4 is animation speed. An animation speed of 1 will change frames every time the program goes through the loop. A speed of 2 every will change frames every two ticks, etc.

Since our sprite file contains multiple animations we need to define where our animations begin and end. (There is no need to do this step if your sprite only has one animation). Let's animate our sprite blinking while facing to the right. The blink begins on frame 7 (starting from 0) and is four frames long, so...

sprite.addAnimation('blinking-right', 7, 4);

The default animation for sprites is to run through every frame of the entire image file. Since our sprite has multiple animations that would look weird, so let's set the current animation to 'blinking-right'.

sprite.changeAnimationTo('blinking-right');

And now we have an animated sprite!

Let's add one more animation and make our sprite turn to the left or right and walk when the left or right arrow keys on the keyboard are pressed.

sprite.addAnimation('blinking-left', 11, 4);
s.keyDown('right', () => {
  sprite.changeAnimationTo('blinking-right');
  sprite.xPos += 2; // will increase sprite's position on x axis by 2 pixels
});
s.keyDown('left', () => {
  sprite.changeAnimationTo('blinking-left');
  sprite.xPos -= 2;
});

Here is the result

Our sprite is floating in the sky. That's strange. Let's make if feel the force of gravity.

sprite.feelGravity(12);
/* A range from about 5 to 20 is good.
5 is moonish gravity. 14 is Earthish. 
30 is Jupiterish. 14 is default.
*/

Oh no! Our sprite is falling! Let's put some ground below it. This time let's use the Tile class. The tile class is similar to the Background class, except it won't necessarily take up the entire background. Let's use this image:

const ground = new s.Tile('./ground.png', s.width / 2, s.height * 0.75, 2, 1);

This will create a tile centered horizontally, 3/4ths the height of the canvas vertically, repeating 2 times on the x axis and 1 time on the y axis. Now let's make the tile into a platform so our sprite won't fall through it.

ground.platform(true);
/*passing a truthy value as an argument will make the object into a block.
That means that objects with gravity will not be able to pass through it from any direction (from above, below, left, or right).
*/

Yay! Our sprite has a platform to stand on. Now let's give it the ability to jump.

s.keyUp('space', () => {
  sprite.jump(200, true); // will make sprite jump 200 pixels.
}); // passing a truthy value as the second arguement in jump method will disable "double jumps", i.e. sprite won't be able to jump again until the jump is complete.

Here's what we have. Not bad! But a little boring. Let's make our game more game-like. We need some kind of obstacle...πŸ€” Let's make a flying circle that will destroy our sprite when they collide.

const evilCircle = new s.Circle(s.getRandomColor(), -30, s.randomBetween(-10, s.height), s.randomBetween(20, 30));
/*
Make a randomly colored circle 30 pixels off to the left of the canvas
at a random height between -10 and canvas height 
and with a random radius between 20 and 30.
*/
evilCircle.velX = 8; // Make the circle travel horitontally 8 pixels per frame.
s.onCollision(evilCircle, sprite, true, () => {
  ground.notPlatform(); // Ground is no longer a platform so our sprite will fall. 😒
  evilCircle.destroy(); // Delete all references to evilCircle.
});
/*A truthy third argument for onCollision method will trigger the callback function 
only once per collision. A falsy value will cause the the callback function
to be triggered every tick of the loop.
*/

In our loop callback let's add this code.

s.loop(function () {
  if (evilCircle.xPos + evilCircle.radius > s.width) {
    // if evil circle goes beyond width of canvas...
    evilCircle.color = s.getRandomColor();
    evilCircle.xPos = -30; // send evil circle back to left side of canvas.
    evilCircle.yPos = s.randomBetween(0, s.height);
  }
  if (sprite.yPos > s.height) {
    sprite.destroy(); // delete all references to our sprite when it has fallen off the canvas. 😒😒😒
  }
});

All together...

import s from './s2pd/s2pd.js';

s.ezSetup();
const clouds = new s.Background('./clouds.png');
clouds.velX = -2;
const sprite = new s.Sprite(s.width / 2, s.height / 2, './hero.png', 35, 4);
sprite.addAnimation('blinking-right', 7, 4);
sprite.changeAnimationTo('blinking-right');
sprite.addAnimation('blinking-left', 11, 4);
s.keyDown('right', () => {
  sprite.changeAnimationTo('blinking-right');
  sprite.xPos += 2;
});
s.keyDown('left', () => {
  sprite.changeAnimationTo('blinking-left');
  sprite.xPos -= 2;
});
sprite.feelGravity(12);
const ground = new s.Tile('./ground.png', s.width / 2, s.height * 0.75, 2, 1);
ground.platform(true);
s.keyUp('space', () => {
  sprite.jump(200, true);
});
const evilCircle = new s.Circle(s.getRandomColor(), -30, s.randomBetween(-10, s.height), s.randomBetween(20, 30));
evilCircle.velX = 8;

s.onCollision(evilCircle, sprite, true, () => {
  ground.notPlatform();
  evilCircle.destroy();
});

s.loop(function () {
  if (evilCircle.xPos + evilCircle.radius > s.width) {
    evilCircle.color = s.getRandomColor();
    evilCircle.xPos = -30;
    evilCircle.yPos = s.randomBetween(0, s.height);
  }
  if (sprite.yPos > s.height) {
    sprite.destroy();
  }
});

Let's give our game a try. There we have it! A working game, albeit a rather stupid one. I think you can do better! What will you create?

API

  • s.width - width of the canvas in pixels
  • s.height - height of the canvas in pixels
  • s.percentLoaded - percent of assets (image and audio files) loaded
  • s.percentImagesLoaded - percent of image files loaded
  • s.percentSoundLoaded - percent of audio files loaded
  • s.mouseX - current x position of mouse
  • s.mouseY - current y position of mouse
  • s.touchX - current x position of touch
  • s.touchY - current y position of touch
  • s.touchMoveX - current x position of touch updated every time touch movement detected
  • s.touchMoveY - current y position of touch updated every time touch movement detected
  • s.touchEndX - last x position of touch
  • s.touchEndY - last y position of touch

🌈**ezSetup()**

Sets your project up quickly with default settings. Creates a canvas element with id 'canvas', sizes canvas to 900x600 on larger screens, sizes to window width and window height on mobile screens, automatically resizes on mobile orientation change, and prevents window movement on canvas touch and use of keyboard arrow keys.

  • ezSetup is not recommended for integration with existing projects as it is likely to change the flow of your document in unexpected ways.
s.ezSetup();

🌈**createCanvas(id, width, height)**

Create a new html5 canvas element.

  • id: {string} id of html5 canvas element.
  • width: {number} width of canvas in pixels.
  • height: {number} height of canvas in pixels.
s.createCanvas('canvas', 900, 600);
// creates a 900x600 canvas

🌈**addCanvas(id, width, height)**

Add canvas context to an existing html5 canvas element.

  • canvas: {object} An existing html5 canvas element.
  • width: {number} width of canvas in pixels.
  • height: {number} height of canvas in pixels.
const myCanvas = document.getElementById('myCanvas');
s.addCanvas(myCanvas, 900, 600);
// adds context to canvas and size 900x600

🌈**resize(width, height)**

Resize canvas element.

  • width: {number} new width of canvas in pixels.
  • height: {number} new height of canvas in pixels.

🌈**backgroundColor(color)**

Change background color of canvas.

  • color: {string} Any valid css color passed as a string.
s.backgroundColor('rgb(140,224,98)');

🌈**canvasOpacity(opacity)**

Change opacity of canvas.

  • opacity: {number} A number between 0 and 1. 0 is invisible. 1 is fully visible.
s.canvasOpacity(0.5);

🌈**stillCanvas(how)**

Prevent window from unwanted movement on user interaction. User interaction (such as touching canvas or using keyboard arrow keys) can often cause window to move in unexpected and unwanted ways.

  • how: {string} 'keyboard' to prevent arrow from moving window. 'touch' to prevent touch of canvas from moving window.
s.stillCanvas();
// prevents window from moving when user is touching canvas or when using arrow keys.
s.stillCanvas('touch');
// prevents window from moving when user is touching canvas only. Essential for drawing applications.
s.stillCanvas('keyboard');
// prevents window from moving when arrow keys are used only.

🌈**onResize(callback)**

What to do on window resize or orientation change.

  • callback: {function} - A function to be executed every time window is resized.

🌈**loop(callback)**

Creates a game loop or animation loop. The loop function is an essential element of most applications. Without it only static images are possible. The computer will run through the loop roughly 60 times per second, executing the callback function each time through. The callback function should contain all tasks that you wanted carried out each go around of the loop.

  • callback: {function} a callback function that will be executed every tick of the loop after all assets have loaded.
 s.loop(function(){
   if (mySprite.xPos >= s.width){ // if sprite's x position is greater than width of canvas
     mySprite.xPos = 0; // send it back to the far left side.
 }
}

🌈**whileLoading(callback)**

Task to be carried out while assets (image/audio files) are being loaded. Callback will be called every tick of the loop until loading is completed.

  • callback: {function} a callback function that will be executed every tick of the loop while assets are loading.
const loadingInfo = new s.Text('red', 'center', 'center', `${s.percentLoaded}%`, 'sans-serif', 32);
s.whileLoading(() => {
  // this function will be called every tick of the loop before all loading is completed.
  // this function is optional!
  loadingInfo.text = `${s.percentLoaded}%`; // continually set text to current percent loaded.
});

function game() {
  // do some fun stuff.
  // this function will be called every tick of the loop after all loading is completed.
}
s.loop(game);

🌈**onFirstTime(callback)**

Tasks to be carried out the first time through the loop after all assets have been loaded.

  • callback: {function} A callback that will be executed the first time and only the first time through the loop after all assets are loaded.
const loadingInfo = new s.Text('red', 'center', 'center', `${s.percentLoaded}%`, 'sans-serif', 32);
s.whileLoading(() => {
  loadingInfo.text = `${s.percentLoaded}%`;
});

s.onFirstTime(() => {
  loadingInfo.destroy(); // destroy loading screen after all assets loaded.
});

function game() {
  // do some cool stuff.
}

s.loop(game);

🌈**stopLoop()**

Stops the loop.

s.stopLoop();

🌈**clear()**

Clears the canvas at the beginning of the loop. If clear is not called the image drawn to the canvas during the previous go through of the loop will remain.

s.clear();

🌈**dontClear()**

Undoes the clear() method. Prevents the image drawn to the canvas during the previous go through of the loop from being cleared.

let randomNumber = Math.floor(Math.random() * 10);
randomNumber === 0 ? s.clear() : s.dontClear();
// clears the canvas if random number is 0, otherwise prevents the canvas from being cleared.

Note: Sprite sheets must be laid out in a single horizontal row with each frame equally sized.

Example ↓

🌈**constructor(xPos, yPos, source, numberOfFrames, animationSpeed)**

  • xPos: {number} Initial x position of your sprite.
  • yPos: {number} Initial y position of your sprite.
  • source: {string} Source file of your sprite sheet.
  • numberOfFrames: {number} Optional. The number of frames in your sprite sheet. Leave blank if your sprite is a single frame.
  • animationSpeed: {number} Optional. Speed of animation. 1 is the fastest possible speed. A speed of 1 will change frames every time through the loop. A speed of 2 will change frames every two times through the loop, etc. Leave blank if your sprite is a single frame.
const bunny = new s.Sprite(s.width/2, s.height/2, './bunny.png', 4,4);
//creates a bunny sprite in the center of the canvas. Sprite sheet has four frames. Frame will change every four times through loop. 🐰

Additional Parameters

  • sprite.playedOnce {boolean} - true if animation has played all the way through once.

Sprite Methods

🌈**addAnimation(name, startFrame, numberOfFrames)**

  • name: {string} a string to call the animation by when changing animations.
  • startFrame: {number} the frame in the sprite sheet where the animation begins (starting with 0 for first frame in sprite sheet).
  • numberOfFrames: {number} the number of frames in the animation.
const bunny = new s.Sprite(s.width / 2, s.height / 2, './bunny.png', 4, 4);
bunny.addAnimation('jump', 3, 1);
// Creates a two frame animation called 'jump'. Begins on frame 3 and continues for 1 frame (until frame 4).

🌈**changeAnimationTo(name)**

Change the sprite's current animation.

  • name: {string} The name of the animation.
bunny.changeAnimationTo('jump');
//changes current animation to 'jump'.

🌈**goToFrame(which)**

Change current animation frame.

  • which: {number} Which animation frame do you want to go to.

🌈**onClick(callback, triggerOnce)**

What to do on mouse click.

  • callback: {function} callback function to be executed when sprite is clicked
  • triggerOnce: {boolean} falsy value - execute the callback function every time sprite is clicked. truthy value - execute callback function only once.
bunny.onClick(() => {
  bunny.changeAnimationTo('jump');
  bunny.jump(200);
}, false);
// bunny will jump 200 pixels high each time iot is clicked.

🌈**onHold(callback)**

What to do when mouse button is held down over object or object is touched for a sustained period of time. Callback will execute each tick of the loop while object is held.

  • callback: {function} callback function to be executed when sprite is held.
bunny.onHold(() => {
  bunny.drag(); // drag and drop the sprite.
});

🌈**drag()**

Drag and drop the sprite. Must be triggered in onHold method.

bunny.onHold(() => {
  bunny.drag();
});

🌈**onMouseOver(callback, triggerOnce)**

What to do when mouse is over object.

  • callback - {function} A callback function to be called whenever mouse is over object.
  • triggerOnce - {boolean} Trigger once while true or trigger continually while true.
 sprite.onMouseOver()=>{
    sprite.updateSize(0.9);
 })

🌈**feelGravity(gravity)**

Make your sprite feel the force of gravity. Will fall unless it lands on a platform.

  • gravity: {number} A number describing the strength of gravity. Higher number is more gravity. Default is 14.
bunny.feelGravity(10);
});

🌈**noGravity()**

Remove a sprite's ability to feel gravity.

rabbit.noGravity();

🌈**jump(howHigh, noDoubleJumps)**

Make object jump. Gravity must be enabled using the feelGravity method for the jump method to work.

  • howHigh: {number} How high to make object jump in pixels.
  • noDoubleJumps: {boolean} Prevent object from jumping when it is not on a platform. Default is false.
rabbit.feelGravity(10);
rabbit.onClick(() => {
  rabbit.jump(200);
}, true);
//Make rabbit jump 200 pixels when clicked. Will not be able to jump again until it has landed on a platform.

🌈**platform(blockify)**

Make sprite into a platform. Objects with gravity will not fall through platforms.

  • blockify: {boolean} Default value is false. If platform is a block objects with gravity will not be able to pass through it either from above, below, or to the sides. If platform is not a block objects will be prevented from passing from above.
const bricks = new s.Sprite(200, 200, './bricks.png');
bricks.platform(true);
//bricks sprite is turned into a platform which objects with gravity will not be able to pass through from any direction.

🌈**notPlatform()**

Disable the sprite as a platform.

bricks.notPlatform();

🌈**trimHitBox(left, right, top, bottom)**

Reduces a sprite's hitbox. The hitbox is the area around the sprite used for detecting collisions and clicks. By default a sprite's hitbox is the size of a single animation frame. This can be reduced using trimHitBox for more precise collision detection.

  • Right: {number} the number of pixels to remove from the sprite's right side.
  • Left: {number} the number of pixels to remove from the sprite's left side.
  • Top: {number} the number of pixels to remove from the sprite's top side.
  • Bottom: {number} the number of pixels to remove from the sprite's bottom side.

🌈**updateSize(howMuch)**

Increase or decrease sprite's size.

  • howMuch: {number} 0.5 for half current size. 2 for twice current size, etc.

🌈**changeCursor(type)**

Change mouse cursor style when mouse is over object.

  • type: {string} Any valid css cursor style. No arguments will change cursor to 'pointer'.
rabbit.updateSize(0.5);
// make rabbit half its current size.

🌈**destroy()**

Remove all references to sprite.

🌈**Additional sprite parameters**

  • velX: {number} - The object's velocity along the x-axis.
  • velY: {number} - The object's velocity along the y-axis.
  • currentFrame: {number} - The frame of the animation currently being displayed.
  • currentAnimationName: {string} - The name of the current animation.
  • opacity: {number} - A number between 0 and 1.
  • width: {number} - *read-only*
  • height: {number} - *read-only*
  • gravity: {number} - *read-only*
  • echo {}> .prettierrc.json
  • dragging: {boolean} - *read-only* - is currently being dragged.
  • jumping: {boolean} - *read-only* - is currently jumping.

Note: Like sprite sheets , tile animations must be laid out in a single horizontal row with each frame equally sized. The Tile class automatically creates a repeated image for a specified number of times both vertically and horizontally. There are a lot of fun things you can do with the Tile class❣️

🌈**constructor(source, xPos, yPos, repeatX, repeatY, numberOfFrames, animationSpeed)**

  • source: {string} Source file of your tile.
  • xPos: {number} Optional. Initial x position of your tile. Default is 0.
  • yPos: {number} Optional. Initial y position of your tile. Default is 0.
  • repeatX: {number} Optional. How many times to repeat the image on the x axis. Default is to repeat for the entire width of the canvas.
  • repeatY: {number} How many times to repeat the image on the y axis. Default is to repeat for the entire height of the canvas.
  • numberOfFrames: {number} Optional. The number of frames in your sprite sheet. Leave blank if your tile is a single frame.
  • animationSpeed: {number} Optional. Speed of animation. 1 is the fastest possible speed. A speed of 1 will change frames every time through the loop. A speed of 2 will change frames every two times through the loop, etc. Leave blank if your tile is a single frame.
const backgroundTile = new s.Tile('./stars.png');
// creates a tile background that covers the entire width and height of the canvas.
const ground = new s.Tile('./ground.png', 50,100, 5, 1);
// creates a tile at coordinates 50,100 that repeats 5 times along the x-axis.
const animatedTileBackground = new s.Tile('./cool-animation.png',false,false,false,false, 10, 4)
// creates an animated tile background that covers the entire width and height of the canvas.

🌈Tile Methods

The tile class shares all methods and parameters with the Sprite class, with the exception of updateSize.

🌈Additional tile parameters

In addition to the parameters the Tile class shares with the Sprite class, the Tile class has a few unique parameters.

  • repeatX: {number} - the number of times the tile repeats along the x-axis.
  • repeatY: {number} - the number of times the tile repeats along the y-axis.
  • innerX: {number} - x position of images within the Tile objects boundaries.
  • innerY: {number} - y position of images within the Tile objects boundaries.
  • innerVelX: {number} - velocity of images within the Tile objects boundaries along the x axis.
  • innerVelY: {number} - velocity of images within the Tile objects boundaries along the y axis.
const hearts = new s.Tile('./hearts.png', 100, 100);
hearts.innerVelX = 3;
// make hearts appear to move 3 pixels to the right within the boundaries of their frame every tick of the loop.

See an example of innerVelX and innerVelY in action πŸ‘‰ Click me!

Note: Like sprite sheets , background animations must be laid out in a single horizontal row with each frame equally sized. The Background class automatically creates an infinitely repeating tile background taking up the entire width and height of your canvas (and infinitely beyond)! Takes the hard work out of creating scrolling backgrounds and parralax effects.

🌈**constructor(source, numberOfFrames, animationSpeed)**

  • source: {string} Source file of your background.
  • numberOfFrames: {number} Optional. The number of frames in your background animation. Leave blank if your background is not an animation.
  • animationSpeed: {number} Optional. Speed of animation. 1 is the fastest possible speed. A speed of 1 will change frames every time through the loop. A speed of 2 will change frames every two times through the loop, etc. Leave blank if your background is not an animation.
const sky = new s.Background('./sky.png', 10,4);
// creates an animated background with 10 frames and a speed of four.

Background Methods

🌈**addAnimation(name, startFrame, numberOfFrames)**

Add an animation to the background.

  • name: {string} a string to call the animation by when changing animations.
  • startFrame: {number} the frame in the sprite sheet where the animation begins.
  • numberOfFrames: {number} the number of frames the animation continues for.
const sky = new s.Background(s.width / 2, s.height / 2, './bunny.png', 4, 4);
sky.addAnimation('rain', 3, 1);
// Creates a two frame animation called 'rain'. Begins on frame 3 and continues for 1 frame (until frame 4).

🌈**changeAnimationTo(name)**

Change the background's current animation.

  • name: {string} The name of the animation.
sky.changeAnimationTo('sunny');
//changes current animation to 'sunny'.

🌈**destroy()**

Remove all references to background.

🌈**Additional background parameters**

  • opacity: {number} - A number between 0 and 1.

⭕️ Circle

🌈**constructor(color, xPos, yPos, radius, thickness)**

  • color: {string} Any valid css color.
  • xPos: {number} Position on the x axis. The xPos of a circle describes the shape's center point.
  • yPos: {number} Position on the y axis. The yPos of a circle describes the shape's center point.
  • radius: {number} Distance from center of circle to its outer edge.
  • thickness: {number} Optional! If present the thickness parameter will make your circle into an outline. Thickness describes the width of the circle's outline.
const myCircle = new s.Circle('rgb(1,2,3)', s.width / 2, s.height / 2, 30, 3);
//creates an outline of a circle with a diameter of 60 pixels in the center of the canvas.

πŸ₯š Ellipse

🌈**constructor(color, xPos, yPos, radiusX, radiusY, rotation, thickness)**

  • color: {string} Any valid css color.
  • xPos: {number} Position on the x axis. The xPos of a ellipse describes the shape's center point.
  • yPos: {number} Position on the y axis. The yPos of a ellipse describes the shape's center point.
  • radiusX: {number} Distance from center of ellipse to its outer edge along the x axis.
  • radiusY: {number} Distance from center of ellipse to its outer edge along the y axis.
  • rotation: {number} Rotation of ellipse in radians. Default is 0.
  • thickness: {number} Optional! If present the thickness parameter will make your ellipse into an outline. Thickness describes the width of the ellipse's outline.
const myEllipse = new s.Ellipse('rgb(1,2,3)', s.width/2,s.height/2,30,60,1,3)
// creates an outline of an ellipse in the center of the canvas with a diameter of 60 along the x axis, 120 along the y axis, rotated 1 radian.

⬛️ Rectangle

🌈**constructor(color, xPos, yPos, width, height, thickness)**

  • color: {string} Any valid css color.
  • xPos: {number} Position on the x axis. The xPos of a rectangle describes the shape's leftmost point.
  • yPos: {number} Position on the y axis. The yPos of a rectangle describes the shape's uppermost point.
  • width: {number} Width of rectangle.
  • height: {number} Height of rectangle.
  • thickness: {number} Optional! If present the thickness parameter will make your rectangle into an outline. Thickness describes the width of the rectangle's outline.
const myRectangle = new s.Rectangle('rgb(1,2,3)', s.width/2, w.height/2, 100, 100);
// creates a 100x100 square with the square's upper-left point in the center of the canvas.

πŸ“ Line

🌈constructor(color, startX, startY, endX, endY, thickness)

  • color: {string} Any valid css color.
  • startX: {number} Starting position on the x axis.
  • startY: {number} Starting position on the y axis
  • endX: {number} End position on the x axis.
  • endY: {number} End position on the y axis.
  • thickness: {number} Width of line.
const myLine = new s.Line('rgb(1,2,3)', 100,100,100,300, 3);
// Creates a 3 pixel-wide vertical line stretching from coordinates (100,100) to (100, 300).

Shape Methods

Note: all members of the Shapes super class share the same methods.

🌈**onClick(callback, triggerOnce)**

What to do on mouse click.

  • callback: {function} callback function to be executed when shape is clicked
  • triggerOnce: {boolean} falsy value - execute the callback function every time the shape is clicked. truthy value - execute callback function only once.
myCircle.onClick(() => {
  myCircle.color = s.getRandomColor();
}, false);
// Will change myCircle's color to a random color each time it is clicked.

🌈**onHold(callback)**

What to do when mouse button is held down over shape or shape is touched for a sustained period of time. Callback will execute each tick of the loop while object is held.

  • callback: {function} callback function to be executed when shape is held.
myRectangle.onHold(() => {
  myRectangle.drag(); // drag and drop myRectangle.
});

🌈**drag()**

Drag and drop the shape. Must be triggered in onHold method.

myRectangle.onHold(() => {
  myRectangle.drag(); // drag and drop myRectangle.
});

🌈**onMouseOver(callback, triggerOnce)**

What to do when mouse is over object.

  • callback - {function} A callback function to be called whenever mouse is over object.
  • triggerOnce - {boolean} Trigger once while true or trigger continually while true.
 circle.onMouseOver()=>{
    circle.color = s.getRandomColor();
 })

🌈**changeCursor(type)**

Change mouse cursor style when mouse is over object.

  • type: {string} Any valid css cursor style. No arguments will change cursor to 'pointer'.

🌈**destroy()**

Remove all references to object.

🌈**Additional shape parameters**

  • opacity: {number} - A number between 0 and 1.
  • velX: {number} - The shape's velocity along the x-axis.
  • velY: {number} - The shape's velocity along the y-axis.

🌈**constructor(color, xPos, yPos, text, font, size, thickness, innerColor)**

Prints text to the canvas. Text may be printed on multiple lines by inserting '\n' into the text parameter.

  • color: {string} Any valid css color.
  • xPos: {number or string} Position of text on x axis. xPos of text descibes leftmost point of text. Enter string 'center' to center on x axis.
  • yPos: {number or string} Position of text on y axis. yPos of text describes uppermost point of text. Enter string 'center' to center on y axis.
  • text: {string} The text to be displayed on screen.
  • font: {string} Any valid font.
  • size: {number} Size of font in pixels.
  • thickness: {number} Optional! Width of font outline.
  • size: {number} Optional! Inner color of text if an outline is present. Any valid css color.
const someText = new s.Text('red', 'center', 'center', 'HELLO! πŸ‘‹ \n I ❀️ you', 'sans-serif', 28, 3, 'green');
someText.center = true;
/*
prints...
  HELLO!πŸ‘‹
  I ❀️ you
... with text object centered on the canvas and text alignment within the text object also centered.
*/

Text Methods

🌈**onClick(callback, triggerOnce)**

What to do on mouse click.

  • callback: {function} callback function to be executed when text is clicked
  • triggerOnce: {boolean} Optional! falsy value - execute the callback function every time the text is clicked. truthy value - execute callback function only once. Default is false.
myText.onClick(() => {
  myText.center = false;
});
// Will uncenter text alignment (align to left) on mouse click.

🌈**onHold(callback)**

What to do when mouse button is held down over shape or shape is touched for a sustained period of time. Callback will execute each tick of the loop while object is held.

  • callback: {function} callback function to be executed when shape is held.
myText.onHold(()=>{
  myText.text = Math.floor(Math.random()*10000));
});
// sets text to a new random number every tick of the loop while text object is held.

🌈**drag()**

Drag and drop the shape. Must be triggered in onHold method.

myText.onHold(() => {
  myText.drag(); // drag and drop myText.
});

🌈**destroy()**

Remove all references to object.

🌈**Additional text parameters**

  • opacity: {number} - A number between 0 and 1.
  • velX: {number} - Velocity along the x-axis.
  • velY: {number} - Velocity along the y-axis.
  • center: {boolean} - True πŸ‘‰ Set text alignment to centered. False πŸ‘‰ Set text alignment to left.
  • leading: {number} - Leading increases space between rows. 1.1 is default. Example πŸ‘‰ *** myText.leading = 2; *** Double spaces text

A note on using web audio

  • Most web browsers will throw an error if you try to load or play audio before the user has interacted with the site.
  • Best practice when using audio is to solicit a mouse click or touch from the user before creating an audio context and loading audio.
  • If you load s2pd through a script tag in your html file's head most features of s2pd are available without a development server, but using audio will require you to be connected to a server.
  • For more info on web audio πŸ‘‰γ€€https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API

🌈constructor(source, volume, loop, playbackRate)

  • source: {string} Audio source file path.
  • volume: {number} Playback volume. A number between 0 and 1. Default is 0.5
  • loop: {boolean} True πŸ‘‰ Loop at end of playback. False πŸ‘‰ Don't loop at end of playback.
  • playbackRate: {number} Speed of playback. 1 is nomrmal speed. 0.5 is half normal speed. 2 is twice normal speed etc. Changing playback rate will also affect pitch on most browsers. Default is 1.
const mySound = new s.Sound('./niceMusic.mp3', 0.3, true, 1);
const startButton = new s.Text('red', 'center', 'center', 'START', 'sans-serif', 32);
startButton.onClick(()=>{
  s.loadAudio(); // loads all audio files associated with the Sound class.
  mySound.play(); // mySound will begin playing when it is loaded.
}, true); //trigger once

s.whileLoading(()=>{
console.log(s.percentLoaded) // print percent of assets loaded to console while loading.
});

s.onFirstTime(()=>{
mySound.play(); // play audio after assets are loaded and loop begins.
});

s.loop(function(){
//do some cool stuff.
});

Sound Methods

🌈s.loadAudio() Global method to load ALL audio files. Percent of audio files (and image files) loaded can be retrieved through the global variable πŸ‘‰ s.percentLoaded

See above example for usage.

🌈play()

Plays audio file.

mySound.play();

🌈**stop()**

Stops audio file.

mySound.stop();

🌈**pause()**

Pause audio file. Will resume where it left off when play is called again.

mySound.pause();

🌈**Additional sound parameters**

  • loaded: {boolean} Is file loaded or not.
  • stopped: {boolean} Is file stopped or not.

Mouse methods. Sprites, tiles, and shapes all have their own mouse methods. Refer to Sprite, Tile, and Shape sections of the API to see mouse methods for each individual class.

🌈**listenForMouse()**

Creates event listeners for mouse. No need to call this method if using ezSetup()

🌈**onClick(callback, triggerOnce)**

What to do on mouse click. Works for "touch clicks" too.

  • callback: {function} - a callback to be execruted on any mouse click.
  • triggerOnce: {boolean} - Truthy value to trigger callback only one time, or falsy value to trigger on every click.
  s.onClick(()=>{
    console.log('🐧');
  })
  //prints a penguin to the console every time user clicks mouse.

🌈mouse variables

  • s.mouseX: current x position of mouse.
  • s.mouseY: current y position of mouse.

🌈**listenForTouch()**

Creates event listeners for touch. No need to call this method if using ezSetup()

🌈**onTouch(callback)**

What to do when user is touching screen. For a touch click use s.onClick() method.

  • callback: {function} - a callback to be executed every tick of the loop while user is touching screen.
const mySprite = new s.Sprite(100, 100, 'cool-image.png');
  s.onTouch(()=>{
    mySprite.xPos = s.touchMoveX;
    mySprite.yPos = s.touchMoveY;
  })
  //move sprite to touch coordinates everytime user moves their finger.

🌈**touch variables**

  • s.touchX: current x position of touch.
  • s.touchY: current y position of touch.
  • s.touchEndX: x position of last touch end.
  • s.touchEndY: y position of last touch end.
  • s.touchMoveX: x position of touch updated every time user moves finger
  • s.touchMoveY: y position of touch updated every time user moves finger

🌈**listenForKeyboard()**

Creates event listeners for keyboard. No need to call this method if using ezSetup()

🌈**keyDown(key, callback, triggerOnce)**

What to do when user is holding keyboard key down.

  • key: {string or number} JS keycode (number) or a string. Example strings β†’γ€€'space' for space key. ',' for comma. 'left' for left arrow. 'g' for g. '1' for 1. etc.
  • callback:{function} function to be called on key down.
  • triggerOnce: {boolean} Optional! Default is false. Trigger callback once while key is down (true), or trigger callback every tick while key is down(false).
s.keyDown('right', ()=>{
  mySprite.xPos += 2;
})
//make sprite move 2 pixels to the right every tick that right arrow key is held down.

🌈keyUp(key, callback)

What to do when keyboard key is released.

  • key: {string or number} JS keycode (number) or a string. Example strings β†’γ€€'space' for space key. ',' for comma. 'left' for left arrow. 'g' for g. '1' for 1. etc.
  • callback:{function} function to be called when key is up.
s.keyUp('space', ()=>{
  mySprite.jump(200);
})
//make sprite jump 200 pixels high when space key is lifted.

🌈**onCollision(obj1, obj2, triggerOnce, callback)**

Triggers a callback function when two game objects collide.

  • obj1: Any s2pd game object.
  • obj2: Any s2pd game object.
  • triggerOnce: {boolean} Trigger callback once while true, or trigger continually while true.
  • callback: {function} A callback function that will be exectued every time object 1 and object 2 collide.
s.onCollision(mySprite, myOtherSprite, true, () => {
  myOtherSprite.destroy();
});
//destroy myOtherSprite on collision with mySprite.

Random numbers

Random numbers are an essential part of game development and digital art. Here are some useful methods for obtaining them. β¬‡οΈŽ

🌈**choose(option1, option2)**

Randomly choose between option 1 and 2.

  • option1: {any}
  • option2: {any}
s.choose(thisSprite, thatSprite);
// returns either thisSprite or thatSprite randomly.

🌈**randomBetween(min, max)**

Returns a random integer between and including min and max.

s.randomBetween(-1, 1);
// will return either -1, 0, or 1

🌈**RandomNoRepeat(arr)**

RandomNoRepeat is a class that creates an object that will return random selections from a list without repeating any previous selections until all items in the list have been selected. After all items have been selected the object will start from the beginning again. get() method returns an item.

  • arr: {array} An array containing items you want returned to you in a randomized order without repetition.
const fruits = ['🍌', '🍎', 'πŸ“', '🍊', '🍈', 'πŸ‰'];
const getFruits = new s.RandomNoRepeat(fruits);
for (let i = 0; i < 12; i++) {
  console.log(getFruits.get());
}
// prints to the console...
// ["🍌"]
// ["🍈"]
// ["πŸ“"]
// ["πŸ‰"]
// ["🍎"]
// ["🍊"]
// ["🍌"]
// ["πŸ“"]
// ["🍈"]
// ["🍊"]
// ["🍎"]
// ["πŸ‰"]

🌈**getRandomColor()**

Will return a random color in rgb format.

console.log(s.getRandomColor());
// in the console πŸ‘‰ 'rgb(29, 201, 144)'

🌈**roundToDecimals(num, howManyDecimals)**

Returns inputed number rounded to decimals.

  • num: {number} The number you want rounded.
  • howManyDecimals: {number} How many decimal places to round the number.
s.roundToDecimals(10 / 3, 3);
// returns 3.333

Example installation with React and WebPack

s2pd works with React. See the example below for usage. I haven't tried, but I think something similar is probably possible with Vue and other frameworks.

npm install s2pd
import React, { useRef, useEffect } from 'react';
import s from 's2pd';

export default function Canvas(props) {
  const theRef = useRef(null);
  useEffect(() => {
    s.addCanvas(theRef.current, 900, 600);
    const circle = new s.Circle('red', s.width / 2, s.height / 2, 30, 3);
    s.listenForKeyboard();
    s.listenForMouse();
    s.listenForTouch();
    s.stillCanvas();
    s.backgroundColor('pink');
    circle.onHold(() => {
      circle.drag();
    });
    s.loop(() => {});
  }, []);
  return <canvas ref={theRef}></canvas>;
}

About

A simple HTML5 canvas / art /2d game library

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published