Tink is an easy-to-use set of interactivity tools for the Pixi rendering engine. You can use Tink to easily create:
- Drag and drop objects.
- Click-able, touch-able buttons with customizable actions.
- A universal pointer object that works for both touch and and the mouse.
- Interactive sprites that behave like buttons.
(Important! This library targets Pixi v3.0.11, which is the most stable version of Pixi, and is the only version I can recommend using. This library will eventually be upgraded for Pixi v4 when the v4 branch matures.)
Setting up
A universal pointer
Pointer interaction with sprites
Drag and drop sprites
Buttons
Making buttons
Making an interactive sprite
Keyboard control
Setting Tink's optional scale
Let's find out how to use Tink.
First, link to the tink.js script in your HTML file.
<script src="tink.js"></script>Then create a new instance of Tink at the beginning of your JavaScript program.
Supply it with a reference to your running PIXI instance and the renderer.view object (the HTML5 canvas).
let t = new Tink(PIXI, renderer.view);The variable t now represents your running Tink instance. Generally you should
create a new Tink instance after all the resources have loaded.
Next call Tink's update method inside your game loop to update all of Tink's
interactive objects each frame. Here's a simple game loop that will do
the trick:
function gameLoop(){
//Start the loop
requestAnimationFrame(gameLoop);
//Update Tink
t.update();
//Optionally, you probably also want to render your root Pixi
//container, the `stage` object, in this loop:
//renderer.render(stage);
}This is what you need to do to get started with the examples ahead.
Tink lets you make a pointer object that automatically figures
out whether the user is interacting with a mouse or with touch.
Use Tink's makePointer method to create a pointer.
pointer = t.makePointer();Usually one pointer will be enough for most games or applications, but you can make as many as you need. (Does your game or application require complex multi-touch interaction with gestures? Then consider using an excellent HTML5 library called hammer.js.
The pointer object has three user-definable methods that you can
program: press, release, and tap. press is triggered when the
left mouse button is pressed down, or the user presses his or her finger
to the device screen. release is triggered when the mouse button is
released, or the user lifts his or her from the screen. tap is triggered
if the left mouse button is clicked, or the user taps the screen.
Here's an example of how you can define these methods on the pointer:
pointer.press = () => console.log("The pointer was pressed");
pointer.release = () => console.log("The pointer was released");
pointer.tap = () => console.log("The pointer was tapped");Also use the tap method to capture mouse clicks.
The pointer also has x and y properties that tell you its position
on the canvas (Pixi's renderer.view.)
pointer.x
pointer.yIt also has three Boolean properties that tell you pointer's current
state: isUp, isDown and tapped.
pointer.isUp
pointer.isDown
pointer.tappedIf you need to hide the pointer for some reason, use the Boolean visible
property.
//Hide the pointer
pointer.visible = false;
//Make the pointer visible
pointer.visible = true;The pointer has a hitTestSprite method that you can use to find out
if the pointer is touching a sprite.
pointer.hitTestSprite(anySprite);If the pointer is within the rectangular area of a sprite,
hitTestSprite will return true.
hitTestSprite will also work with circular sprites. Just add a property
to a sprite called circular and set it to true.
anyCircularSprite.circular = true;This flags hitTestSprite to use a circular collision detection algorithm
instead of the default rectangular one. If you want to display a hand icon
while the pointer is over sprite the you can set the pointer's
cursor property to "pointer". Setting it "auto" when the pointer
leaves the sprite's area will display the default arrow icon. Here's some
sample code you could use inside your game loop to enable this
feature.
if (pointer.hitTestSprite(anySprite)) {
//Display a hand icon while the pointer is over the sprite
pointer.cursor = "pointer";
}
else {
//Display the default arrow icon when the
//pointer moves outside the sprite's area
pointer.cursor = "auto";
}pointer.cursor just references the HTML5 canvas's element's
style.cursor property to achieve this, using two standard values
from the HTML5 spec: "pointer" and "auto". You can assign any cursor style
value that you like. (A web search for "HTML style.cursor" will turn up a complete
list of possible values.) You can also set this manually if you want
to through Pixi's renderer.view object. Here's how:
renderer.view.style.cursor = "cursorStyle";These cursor styles will only be visible on a mouse-based interface; on a touch interface they're ignored.
You can add drag-and-drop functionality to a sprite with Tink's
makeDraggable method. Just supply it with a single sprite, or a list of
sprites, that you want to make draggable.
t.makeDraggable(cat, tiger, hedgehog);You can then use the mouse or touch to drag the sprites around the canvas.
When you select a draggable sprite, its stacking order changes so that it appears above the other sprites. The mouse's arrow icon also changes to a hand when its over a draggable sprite.
Draggable sprites have a Boolean property called draggable that is
set to true. To disable dragging, set draggable to false.
anySprite.draggable = false;Setting it back to true will enable dragging again.
To completely remove a sprite (or list of sprites) from the drag-and-drop
system, use the makeUndraggable method, like this:
t.makeUndraggable(cat, tiger, hedgehog);Drag-and-drop is a fundamental interactive feature that can be used as the basis for making puzzles games, matching games, or sophisticated user interfaces.
Buttons are an important UI component that you'll definitely want to use in your games and applications. Tink has a useful button method that lets you quickly create them. Before I show you how to make buttons, lets first find out what buttons actually are, can how you can use them.
You can think of buttons as "clickable/touchable sprites". The most important thing you need to know about buttons is that they have states and actions. States define what the button looks like, and actions define what it does. Most buttons have three states:
- Up: When the pointer is not touching the button.
- Over: When the pointer is over the button.
- Down: When the pointer is pressing down on the button.
Touch-based interfaces need only two states: up and down.
With the button object that you'll learn to make in the next section,
you be able access these states through the button's state property, like this:
playButton.stateThe state property could have the string value "up", "over",
or "down", which you could use in your game logic.
Buttons also have actions:
- Press: When the pointer presses the button.
- Release: When the pointer is released from the button.
- Over: When the pointer moves into the button's area.
- Out: When the pointer moves out of the button's area.
- Tap: When the button has been tapped (or clicked.)
You can define these actions as user-definable methods, like this:
playButton.press = () => console.log("pressed");
playButton.release = () => console.log("released");
playButton.over = () => console.log("over");
playButton.out = () => console.log("out");
playButton.tap = () => console.log("tapped");In the button object that we'll make ahead, you'll able to access the button's "pressed" and "released" actions in a string property, like this:
playButton.actionGot it? Good! So how do we actually make buttons?
First, start with three images that define the three button states. You might call them "up.png", "over.png", and "down.png". Then add those three images to a tileset, or as frames in a texture atlas.
Although having three image states is standard, sometimes buttons have only two image states. This is particularly true of touch-only buttons, which don't have an "over" state. The button object that we're going to make ahead will use three images if they're available, but if it only has two, Tink will assign these to the "up" and "down" states.
Next, publish the texture atlas and load it into your program:
PIXI.loader
.add("images/button.json")
.load(setup);Then in the setup function where you initialize your sprites, create an array that
references each of the three button frames in this order: up, over and down.
function setup() {
//Create an alias for the texture atlas frame ids
let id = PIXI.loader.resources["images/button.json"].textures;
let buttonFrames = [
id["up.png"],
id["over.png"],
id["down.png"]
];
}These don't have to be frame ids: you can use an array of any Pixi textures, like single image textures if you want to.
Finally, use Tink's button method to create the button. Supply the
buttonFrames array as the first argument.
let playButton = t.button(buttonFrames, 32, 96);The second and third optional arguments are the button's x and y position.
And don't forget to add the button to the stage (Pixi's root
container object)!
stage.addChild(playButton);You now have a useful button object that you can use in any game or application.
At its heart, a button is just an ordinary Pixi MovieClip with extra properties
and methods, so you can treat it like any other MovieClipobject.
Tink has another useful method called makeInteractive that lets you add button
properties and methods to any ordinary sprite.
t.makeInteractive(anySprite);This lets you turn any sprite into a button-like object. You can now
assign press or release methods to the sprite, and access its
state and action properties, like this:
anySprite.press = () => {
//Do something when the pointer presses the sprite
};
anySprite.release = () => {
//Do something when the pointer is released after pressing the sprite
};If you want any sprite to behave like a button, use makeInteractive!
keyboard is a method that listens for and captures keyboard events. It's really
just a convenient wrapper function for HTML keyup and keydown events
so that you can keep your application code clutter-free and easier to write and read.
Here's how to use the keyboard method. Create a new keyboard object like this:
let keyObject = t.keyboard(asciiKeyCodeNumber);Its one argument is the ASCII key code number of the keyboard key
that you want to listen for. Here's a list of ASCII key codes you can
use.
Then assign press and release methods to the keyboard object like this:
keyObject.press = () => {
//key object pressed
};
keyObject.release = () => {
//key object released
};Keyboard objects also have isDown and isUp Boolean properties that you can use to
check the state of each key.
Tink has another convenience method called arrowControl that lets you
quickly create a 4 direction controller for sprites using the
keyboard arrow keys. It's useful for quickly prototyping game ideas.
Supply the arrowControl method with the sprite you want to control
and the pixels per frame that you want it to move:
t.arrowControl(anySprite, 5);Then just update your sprite's velocity inside your game loop, like this:
function gameLoop() {
requestAnimationFrame(gameLoop);
anySprite.x += vx;
anySprite.y += vy
}You'll then be able to move the sprite in all four directions using
the arrow keys.
(For the arrowControl method to work, your sprite needs to have vx and vy properties
that refer to the sprite's velocity.)
You now know everything you need to know about using Tink, so go ahead and start using it! But, if you want to re-size or re-scale Pixi's renderer inside the browser window, there's one more thing you need to know.
You can use an optional helper function called scaleToWindow that
will automatically scale your Pixi renderer to the maximum size of
the browser window. Visit scaleToWindow's source code repository to
find out how to use it. Tink's constructor has an optional second argument: scale. The
default scale value is 1, but if you've re-scaled the canvas using
the scaleToWindow function, supply scaleToWindow's return value.
Here's an example of what this might look like:
First, run scaleToWindow and capture the returned scale value at the beginning of your program.
let scale = scaleToWindow(renderer.view);Next, create a new instance of Tink and supply the scale value as the second argument in the constructor.
let t = new Tink(PIXI, renderer.view, scale);This will ensure that the coordinates that Tink uses will match the canvas's scaled pixel coordinates.
Finally, make sure to update the Tink scale if you also opted for rescaling the canvas element every time the size of the browser window has changed:
window.addEventListener("resize", function(event){
let scale = scaleToWindow(anyCanvasElement);
t.scale(scale)
});


