alexcurtis

Tweening a Tint

Recommended Posts

Hi,

 

Is it possible to tween a tint? So going from the default 0xFFF to a slightly darker tint 0x757575 over a period of time.

 

Ive tried:

tween.to({ tint: 0x757575 }, 5000, Phaser.Easing.Linear.None);

But it seems to cycle through R,G,B or something to create some crazy colours until it reaches the end tint.

 

Really all I want to do is darken the sprite over a period of time.

 

Many Thanks,

-Alex

Share this post


Link to post
Share on other sites

You could have a routine that sets the tint like this

tint = '#' + r + g + b;

and then tween r,g, and b as separate 2-byte hex values

 

Is it possible to plumb a routine callback into a tween? -- or is it on the update method?

 

I could probably just override the sprite update call and pull in the tint. Would that be efficient? 

 

EDIT: Thanks wayfinder. I think I have a decent solution using your suggestion.

Share this post


Link to post
Share on other sites

The Phaser.Color object has a range of different tools for this kind of thing, such as interpolateColor - with a bit of imagination you can create a function to let you tween colours, like this:

function tweenTint(obj, startColor, endColor, time) {    // create an object to tween with our step value at 0    var colorBlend = {step: 0};    // create the tween on this object and tween its step property to 100    var colorTween = game.add.tween(colorBlend).to({step: 100}, time);        // run the interpolateColor function every time the tween updates, feeding it the    // updated value of our tween each time, and set the result as our tint    colorTween.onUpdateCallback(function() {      obj.tint = Phaser.Color.interpolateColor(startColor, endColor, 100, colorBlend.step);       });        // set the object to the start color straight away    obj.tint = startColor;            // start the tween    colorTween.start();}

Use like so:

tweenTint(sprite, 0xff0000, 0x0000ff, 2000); // tween the tint of sprite from red to blue over 2 seconds (2000ms)

Share this post


Link to post
Share on other sites

 

The Phaser.Color object has a range of different tools for this kind of thing, such as interpolateColor - with a bit of imagination you can create a function to let you tween colours, like this:

function tweenTint(obj, startColor, endColor, time) {    // create an object to tween with our step value at 0    var colorBlend = {step: 0};    // create the tween on this object and tween its step property to 100    var colorTween = game.add.tween(colorBlend).to({step: 100}, time);        // run the interpolateColor function every time the tween updates, feeding it the    // updated value of our tween each time, and set the result as our tint    colorTween.onUpdateCallback(function() {      obj.tint = Phaser.Color.interpolateColor(startColor, endColor, 100, colorBlend.step);       });        // set the object to the start color straight away    obj.tint = startColor;            // start the tween    colorTween.start();}

Use like so:

tweenTint(sprite, 0xff0000, 0x0000ff, 2000); // tween the tint of sprite from red to blue over 2 seconds (2000ms)

 

Very Nice. Thank you so much lewster32. :-)

Share this post


Link to post
Share on other sites

Thanks for the cool method, lewster32, it is awesome. It works perfect on Chrome, Firefox and Edge on Phaser 2.4.4. For those of you using TypeScript, here is the code:

private tweenTint(spriteToTween:Phaser.Sprite, startColor:number, endColor:number, duration:number):void {
    var colorBlend = {step: 0};

    this.game.add.tween(colorBlend).to({step: 100}, duration, Phaser.Easing.Default, false)
        .onUpdateCallback(() => {
            spriteToTween.tint = Phaser.Color.interpolateColor(startColor, endColor, 100, colorBlend.step, 1);
        })
        .start()
};

You can also add an onComplete event at the end to nullify the colorBlend object (I am not sure whether it is fully removed from memory).

Share this post


Link to post
Share on other sites

For anybody looking at this in ES6, it works fine.. I've also added arrow functions for lexical scope. It also defaults time value to 250ms if none set, and will callback if a valid function or method has been passed. Tested and works like a charm!!

 

// in boot state...
this.game.animations = new Animations(this.game);
// in Animations class

export default class Animations {
    constructor(game) {
        this.game = game;
    }

    tweenTint(obj, startColor, endColor, time = 250, callback = null) {
        if (obj) {
            let colorBlend = { step: 0 };
            let colorTween = this.game.add.tween(colorBlend).to({ step: 100 }, time);
            colorTween.onUpdateCallback(() => {
                obj.tint = Phaser.Color.interpolateColor(startColor, endColor, 100, colorBlend.step);
            });
            obj.tint = startColor;
            if (callback) {
                colorTween.onComplete.add(() => {
                    callback();
                });
            }
            colorTween.start();
        }
    }
}
// in any class

create() {
    let sprite = this.game.add.sprite(x, y, key, frame);
    let tint = this.game.animations.tweenTint(sprite, '0xffffff', '0xff0000', 200, () => {
        this.tweenCompleteFunction();
    });
}

tweenCompleteFunction() {
    console.log('tween has finished');
}

 

Share this post


Link to post
Share on other sites

Just out of interest, why use arrow functions? It makes no sense in your library code and is (currently) far less performant, not to mention it nukes the call stack.

This answer from SO addresses some of the dangers of using them liberally like this when they aren't required. The second one, used for the `add`, simply pass `callback`  straight into the `onComplete` handler, you dont need any extra function there.

Probably worth mentioning that currently your code will only work in Chrome and Firefox and nowhere else, for people that are just trying to copy-paste it into their code.

Share this post


Link to post
Share on other sites

Thanks for the tips on ES6. I'm still fairly new to this all! Thanks for the link on the correct usage of arrow functions. I was toying with the idea of using Phaser's Callback system, however as Rich said he was thinking of scrubbing it from lazer 3.0, I decided to pass a callback function. 

May I ask how or why it will only work in Chrome and Firefox? Thanks for the help and info!!

Share this post


Link to post
Share on other sites

Arrow functions are not supported by Safari or IE and default function parameters are supported only by Chrome & Firefox. Arrows are in Edge and I'd imagine Safari will catch up fairly soon, but default function parameters are going to take a little longer, hopefully not too much longer.

Are you using babel (or something else) to transpile?

Arrow functions and scope are tricky, trickier than most people assume. Arrow functions actually have no scope, so they grab it from their parent closure, although grab it is a poor term. This is a better explanation. I use them regularly, and not always correctly either, but its worth knowing about the implementational oddities of arrow functions (similar things are known as lambdas in a host of other languages).

Share this post


Link to post
Share on other sites

Thank you for the link! I appreciate the reply! Taking my code to the next 'level', pardon the pun ;) really is the next thing on my list, making sure that the code I write has been written in the best way without affecting performance.

I owe you a beer I think :D

Share this post


Link to post
Share on other sites

I have a question @mattstyles, I used the arrow functions to keep lexical scope on the callback, if I remove this like so: 

// replace the code below
if (callback) {
    colorTween.onComplete.add(() => {
        callback();
    });
}

// with this
if (callback) {
    colorTween.onComplete.add(callback());
}

The callback function is not called, or if it is.. it's lost scope. Would you do this the old fashioned way? 

if (callback) {
    colorTween.onComplete.add(function() {
        callback();
    }, this);
}

Thanks!

Share this post


Link to post
Share on other sites

What the doc said :) 

This is an example of passing a function around. As you're in this for the long haul, check out stuff on passing functions.

Sometimes you will see something like:

mappedList = myList.map( myMapFunction( 'foo' ) )

This is an example of invoking a function, whereby its return will be passed to the callee, in this case `myList.map`. `myMapFunction` would look something like:

function myMapFunction( name ) {
  // `name` is available here
  return function( item ) {
    // Do stuff
    // `name` is available here, for example
    return item + name
  }
}

In this case we are passing a parameter through `myMapFunction` so that it can be used in the array map function. In this case the output of all this jazz is to mutate `myList` into a new list that has `foo` appended to each string in the array.

You could achieve the same end result by extracting `name` to a variable and using an anonymous function inside the map, but, crucially, you lose two extremely important things: 1) you lose function purity and 2) you lose the ability to compose functions.

This example is probably best known as a `thunk`.

You might want to check out thunks, function passing, functional purity (referential transparency) and functional composition. In this case most of this comes from functional programming (or even functional reactive programming) but JS is a great platform for FP/FRP and getting into habits of writing code like this generally makes is more robust, testable, composible and reliable.

 

Share this post


Link to post
Share on other sites

Thanks for the tips, I most definitely owe you a beer or 6! I've just ordered some new books on the above topics, figure it's about high time I get on top of this once and for all! Can you recommend any decent online resources?

Cheers! 

Share this post


Link to post
Share on other sites

Addy's book on patterns is a little old now but a great reference, covers the basics and most of the GoF stuff.

Other than that I use Github to follow useful projects and poke through code and if I can get hold of the devs (through opening an issue for something specific, or sometimes via Twitter or email) then I'll ask as many questions as I can, many devs are receptive to polite questions and will happily write you back a paragraph or two, obviously good devs are busy so don't take silence as rudeness.

Forums like this one are a great place and many devs I follow write technical blogs, or use Medium, so you can pick up a lot about implementation decisions from there, usually their posts have a little advertising swing but its useful to hear them chat about the projects that you want to use anyway.

Share this post


Link to post
Share on other sites

@mattstyles Thanks for the info! I've actually already got that book, it's sat on my shelf, I've not had time to go through it yet! About time I dust it off I think ;)

Also, just to clarity, I tried the code in the other browsers and it works fine,then it dawned on me, I forgot to mention the fact I am using Babel, so all the es6 works fine in all browsers! Thanks for taking the time to write decent responses! I greatly appreciate it!

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now


  • Recently Browsing   0 members

    No registered users viewing this page.