Linearly interpolating between two UIColor’s

Published on 17/06/2013

Today I wrote a simple Objective-C function to interpolate between two UIColor’s. Since it’s something I’ve needed in the past on several occasions I figured I’d put it here for reference. Hopefully it helps someone else out in the future.

- (UIColor *)colorLerpFrom:(UIColor *)start
                        to:(UIColor *)end
                        withDuration:(float)t
{
    if(t < 0.0f) t = 0.0f;
    if(t > 1.0f) t = 1.0f;
 
    const CGFloat *startComponent = CGColorGetComponents(start.CGColor);
    const CGFloat *endComponent = CGColorGetComponents(end.CGColor);
 
    float startAlpha = CGColorGetAlpha(start.CGColor);
    float endAlpha = CGColorGetAlpha(end.CGColor);
 
    float r = startComponent[0] + (endComponent[0] - startComponent[0]) * t;
    float g = startComponent[1] + (endComponent[1] - startComponent[1]) * t;
    float b = startComponent[2] + (endComponent[2] - startComponent[2]) * t;
    float a = startAlpha + (endAlpha - startAlpha) * t;
 
    return [UIColor colorWithRed:r green:g blue:b alpha:a];
}

Reports UI

Published on 6/01/2013

As mentioned previously, part of the reason for developing my diabetic journal application alone was to help give me a better understanding of UI design. I wanted to really sweat the details and use the experience to help improve my rather limited skill set.

But as I’ve found, the tricky part about design is that what works well for you may seem entirely alien to other people. Which is why, last night, I took to Twitter looking for opinions on one of two options for a reports UI element: paginated results, or an infinitely scrolling ticker tape.

Been noodling with this forever, so help me out! Which do you prefer? Paginated results (left), or infinite scrolling? http://cl.ly/image/0z31060g1140 – @nialgiacomelli

The problem

Opinions were fairly mixed. The majority of people liked the cleanliness of the ticker tape approach because it was minimal and wouldn’t occupy much screen space. But I had a few concerns:

  • Discoverability was problematic. Would people know to scroll horizontally for more information?
  • Would the motion feel alien due to the fact that the ticker tape was sitting inside of a vertically scrollable table?
  • Because of the nature of the reports, it was likely that users would view them looking for a specific piece of information. Would they be able to find what they were looking for easily in the ticker tape, or would they become disorientated?

Then there was the paginated results. They were clear, and any information would be easily discoverable. But it seemed almost nonsensical to place them front-and-centre. I would be showing less of the information the user came to see in the vast majority of situations. But on the other hand, the reports provided an overview of the data below, making it a natural fit to have them towards the top of the screen.

My solution

After sleeping on it, I decided that discoverability was an important enough reason to ditch the ticker tape approach altogether. Instead, I decided to use the paginated results but hide the reports by default. This meant that unless the user was specifically looking to view a report, no screen space was spared. To view a report, I hacked up a hybrid of the ‘pull to refresh’ mechanic that acts a bit like a pull down projector screen.

UI preview

By pulling the tableview down below a certain threshold and releasing your drag, the report view will animate into view. You’re then free to flick between it’s various pages and read the data there. If you wish you can continue to scroll down the page, leaving the report visible at the top.

Alternatively, if you’re done reading the report and would like it to get out of your way, you simple have to pull the tableview down again to release it. Like a projector screen, it’ll whip up and hide away.

I’ll be interested to see what testers make of it in the coming weeks.

New Years resolutions

Published on 1/01/2013

Among other New Years resolutions, this year I promised to start taking better care of myself. As a Type 1 diabetic, it’s fairly easy to live in a state of withdrawal, or in some instances severe depression, regarding the state of your health.

Which is why, late last year, I started making a real push to check my blood glucose levels more frequently. For those who don’t know, active monitoring of blood glucose levels is one of the best tools a diabetic has at their disposal for helping to gauge the status of their health on a daily basis.

“In a disordered mind, as in a disordered body, soundness of health is impossible.” – Cicero

But I found that as I became more informed, I began to want more context. What had I eaten to make my sugars spike so high, or fall so low? Had any recent physical activity had any effect? What dosage of my medication had I taken that day as opposed to this one?

In essence, I found myself looking for a diabetic journal. Something that could show me detailed entries on a daily basis, but also pull back to show my progress over time. I wanted something that I could keep in my pocket, something that could give me an at-a-glance indication of my health, or allow me to dig down deeper for specifics. And because I couldn’t find anything that fit my needs on the App Store, I decided to build one myself.

While I’ve worked with a number of insanely talented graphic designers in the past, I decided that this was a project I wanted to keep close to home. Because the very act of designing the application has forced me to open my eyes to areas of diabetic medicine and lifestyle that I had never even so much as considered.

I’ve read documents on the scientific process of generating blood glucose readings, the differences in international units and measurement, the myriad ways doctors have developed for the delivery of insulin and other drugs, and the (frankly) nebulous world of controlled diets and calorie counting.

It’s my hope that within the next week or so I’ll be able to send out a limited beta to friends and fellow diabetics for their consideration. If others find as much value in it as I have, you may well see it on the App Store soon.

How to calculate the angle necessary to hit a target, and track a projectile along a given trajectory

Published on 8/09/2012

Recently I needed to write some logic to determine the launch angle necessary for a projectile to hit a target given a constant velocity, and to then track it’s progress along that trajectory. It’s a problem I’ve had multiple times in the past, so I thought I’d write up some javascript code to help others in a similar situation.

The code mentioned below makes use of the following constants and helper functions. Feel free to tweak gravity where necessary:

var gravity = 9.6;
 
function Vector(x_, y_)
{
    this.x = x_;
    this.y = y_;
}

First up is the calculateLaunchAngle function. Pass this a position vector, a target vector and a constant velocity to determine the angle of launch necessary to hit the chosen target. You’ll notice that this function generates two possible angles: the most direct trajectory, and the most verbose. It’s also possible for calculateLaunchAngle to return null when hitting the target is impossible.

function calculateLaunchAngle(position, target, velocity)
{
	var targetX = target.x - position.x;
	var targetY = -(target.y - position.y);
	var r1 = Math.sqrt(Math.pow(velocity, 4) - gravity*(gravity*(Math.pow(target.x, 2)) + ((2*target.y)*(Math.pow(velocity,2)))));
	if (!isNaN(r1)) 
	{
		var a1 = (Math.pow(velocity, 2) + r1)/(gravity*target.x);
		var a2 = (Math.pow(velocity, 2) - r1)/(gravity*target.y);
		a1 = -Math.atan(a1);
		a2 = -Math.atan(a2);
		if (targetX < 0)
		{
			a1 -= 180/180*Math.PI;
			a2 -= 180/180*Math.PI;
	    }
		return a1; //or a2
	}
	return null;
};

Next up we have a calculateTrajectory function, which is helpful for calculating the position of a projectile as it travels along a given trajectory. You’ll need the initial launch angle and velocity, as well as the time elapsed since launch.

function calculateTrajectory(launchAngle, launchVelocity, time)
{
	var vx = launchVelocity * Math.cos(launchAngle); // initial velocity in x
	var vy = launchVelocity * Math.sin(launchAngle); // initial velocity in y
 
	var x = (vx * time);
	var y = (0.5 * gravity * time + vy) * time;
 
	return new Vector(x, y);
};

Configuring Amazon’s RDS to allow stored procedures on a Mac

Published on 19/07/2012

It’s likely that if you’ve tried to create a stored procedure on a vanilla Amazon RDS instance you will have seen the following error message:

You do not have the SUPER privilege and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)

Unfortunately setting the log_bin_trust_function_creators isn’t possible via Amazon’s AWS console. Attached below are the steps I had to take to get stored procedures running:

  1. Grab Amazon’s RDS Command Line Tools, rename the unzipped directory to RDSCli and copy it somewhere it won’t be disturbed (I chose the root of my hard drive).
  2. Next, edit (or create) your ~/.bash_profile, like so:

    nano ~/.bash_profile

    And enter the following (note that you should change your EC2_REGION to something more applicable):

    export AWS_RDS_HOME=/RDSCli
    export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home
    export PATH=$PATH:${AWS_RDS_HOME}/bin
    export AWS_CREDENTIAL_FILE=${AWS_RDS_HOME}/awscredentials
    export EC2_REGION=us-east-1
  3. Next you’ll want to rename the credential-file-path.template file in your RDSCli directory and call it something like awscredentials. Open it up in your text editor of choice and follow the commented instructions to include your AWS credentials.
  4. You should now be ready to use the RDS command line tools. Run the following commands to test everything has been configured correctly:

    echo $JAVA_HOME
  5. If everything’s gone according to plan, this should echo out the Java path you entered in your .bash_profile. Next we’ll create a new parameter group and turn the log_bin_trust_function_creators flag to ON.

    rds-create-db-parameter-group admin-params -f mysql5.5 -d "Administrative Params"
    rds-modify-db-parameter-group admin-params --parameters="name=log_bin_trust_function_creators, value=on, method=immediate"
  6. Once that’s done, we tell our database instance to run with our new parameter group (note that you’ll need to change dbinstance to match your instance name):

    rds-modify-db-instance dbinstance --db-parameter-group-name=admin-params
  7. Finally, tell your database instance to reboot:

    rds-reboot-db-instance dbinstance

And that’s it! Note that you may need to add the following flag to any SQL-based CREATE functions:

DEFINER=CURRENT_USER

Lumicon coverage

Published on 8/07/2012

Lumicon has been available on the App Store for just over two weeks, so I thought it was about time I sat down to give an overview of how the launch went and how the game has been received overall.

First things first, here’s some of the press coverage we received:

Perhaps the only thing more gratifying than seeing positive reviews of the game crop up on major gaming websites has been the response from customers. Nothing brought it home better than hearing from people who’d taken the time to buy the game and give it a try. We received endorsements from a well known grime MC, saw complete strangers boasting about their scores on Twitter, and even received emails from a customer who had played for 4 hours straight to achieve a highscore of 190k.

In closing, I’d just like to say a big thank you to everyone who’s taken the time to play Lumicon. We worked hard on it, and we’re glad you guys seem to be enjoying it!

LIF: Meet Max

Published on 7/07/2012

I’m working on a new project. Nothing huge, but something I’ve wanted to do for a long time. Right now I’m busy wrapping up the next Lumicon update, but will shortly begin development work on what I’ve codenamed LIF.

As with most of my projects, I’m treating it as a learning experience. Particularly I’m looking to explore the act of storytelling and character development. While gameplay will be fairly simple, it’s my hope that the underlying story will convey a deeper message – one that carries a rich emotional cadence.

In the spirit of exploration, rather than reach out to artistic friends, I’ve decide to attempt to create all of the games art assets myself. I’m by no means accomplished in the field, but after a bit of soul searching decided that creating everything by hand would help make the project feel more authentic (or at the very least more earnest).

While I’m not prepared to share too much of the story at this point, I did want to introduce some early work on one of it’s main protagonists. He’s a stray beagle named Max, and he’ll be who you control throughout most of the game.

A simple way to archive iTC sales reports

Published on 17/06/2012

Although there are already a number of great reporting tools available for iTunes Connect, I wanted a simple script that I could use to fetch and archive hardcopies of daily sales data. So, in an attempt to re-invent the wheel (but hopefully learning something in the process), I decided to write one myself. You’ll find it attached below and also over at GitHub.

I’m fairly green when it comes to shell scripting, so I’d welcome any criticism or feedback.

#! /bin/sh
# ------------------------------------------------------------
#
# Uses Apple's Autoingestion Java class to fetch
# daily iTunes Connect reports from X days ago
# by Nial Giacomell (http://nial.me)
#
# requires: Autoingestion.class found at:
# http://www.apple.com/itunesnews/docs/AppStoreReportingInstructions.pdf
#
# usage: sh itcreportfetch.sh days_ago
# 
# ------------------------------------------------------------
 
# Use the following iTunes Connect credentials
USERNAME='email'
PASSWORD='password'
VENDOR_ID='8XXXXXXX'
 
fetchReport(){
	# Check that we're not being passed a bogus days_ago value
	if [ $1 -gt 0 -a $1 -lt 16 ]
	then
		export SECONDS=`expr $1 \\* 86400`
		local TIMESTAMP=$(perl -e 'use POSIX;print strftime "%Y%m%d",localtime time-$ENV{"SECONDS"};')
 
		local TSYEAR=${TIMESTAMP:0:4}
		local TSMONTH=${TIMESTAMP:4:2}
		local TSDAY=${TIMESTAMP:6:2}
 
		# Query auto-ingest
		cd $(dirname $0)
		$(java Autoingestion $USERNAME $PASSWORD $VENDOR_ID Sales Daily Summary $TIMESTAMP > /dev/null)
 
		COMPRESSED_FILENAME=S_D_$VENDOR_ID\_$TIMESTAMP.txt.gz
		UNCOMPRESSED_FILENAME=S_D_$VENDOR_ID\_$TIMESTAMP.txt
 
		# Uncompress and archive the report in question
		if [ -f $COMPRESSED_FILENAME ]
		then
			gzip --uncompress $COMPRESSED_FILENAME -f
			mkdir -p $TSYEAR/$TSMONTH
			mv $UNCOMPRESSED_FILENAME $TSYEAR/$TSMONTH/$TSDAY.txt
		fi
	else
		echo "Invalid days_ago value specified. Must be within valid range (1 - 15)"
		exit 1
	fi
}
 
# Fetch our report
fetchReport $1

Porting Photoshop blend modes to cocos2d

Published on 5/06/2012

It’s fairly common for game developers to work with graphical artists who make use of Photoshop for mockups and artwork. Occasionally it’s necessary to mimic one or more of Photoshop’s blend modes to accomplish a visual effect. Thankfully, as part of cocos2d’s CCBlendProtocol we can accomplish this using the setBlendFunc method.

The setBlendFunc acts as a wrapper around OpenGL ES’ glBlendFunc and accepts two GLenum’s as parameters, like so:

GLenum src = GL_ONE;
GLenum dst = GL_ONE;
[sprite setBlendFunc:(ccBlendFunc){src, dst}];

There are already a number of great resources available for experimenting with OpenGL blend modes, but for ease of reference I’ve included a table of the most commonly used Photoshop blend modes and their setBlendFunc equivalents:

Photoshop Blendmode SRC DST
Blend GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
Multiply GL_DST_COLOR GL_ONE_MINUS_SRC_ALPHA
Screen GL_ONE_MINUS_DST_COLOR GL_ONE
Linear Dodge GL_ONE GL_ONE

If you’ve experimented with other Photoshop blend modes using cocos2d’s setBlendFunc and have corrections or additions you’d like to make to the above table, please get in touch on Twitter @nialgiacomelli

Pausing and resuming sound effects in cocos2D

Published on 4/06/2012

cocos2d comes with a handy OpenAL wrapper called SimpleAudioEngine that helps simplify the act of playing background music and sound effects on the iPhone. Strangely, while the SimpleAudioEngine class offers users the ability to play and stop sound effects, it offers no option to pause or resume them.

To add pause and resume support to SimpleAudioEngine we have to dig into the belly of the CDSoundEngine class, which can be found in CocosDenshion.m. Let’s start by adding the following two methods:

- (void)pauseAllSounds 
{
	for (int i=0; i < sourceTotal_; i++) 
	{
		ALint state;
		alGetSourcei(_sources[i].sourceId, AL_SOURCE_STATE, &state);
		if(state == AL_PLAYING) 
		{
			alSourcePause(_sources[i].sourceId);
		}
	}	
	alGetError();
}	
- (void)resumeAllSounds 
{
	for (int i=0; i < sourceTotal_; i++) 
	{
		ALint state;
		alGetSourcei(_sources[i].sourceId, AL_SOURCE_STATE, &state);
		if(state == AL_PAUSED) 
		{
			alSourcePlay(_sources[i].sourceId);
		}
	}	
	alGetError();
}

Next, since the SimpleAudioEngine class keeps its reference to CDSoundEngine private, we’ll need to add the following methods to SimpleAudioEngine:

- (void)pauseAllEffects 
{
	[soundEngine pauseAllSounds];
}
- (void)resumeAllEffects 
{
	[soundEngine resumeAllSounds];
}

And that’s it! Well, almost…

One last gotcha

You may notice that, occasionally, while playing and resuming sound effects, sounds may begin to seemingly rewind or to play at random. This is down to CDSoundEngine’s handling of source indexes. Without going into too much detail, CDSoundEngine takes steps to ensure that only a finite number of sounds can be played concurrently on the iPhone. When a sound is played, it’s assigned a referential index. The problem is that, by default, CDSoundEngine doesn’t take the AL_PAUSED state flag into consideration when recycling indexes.

Thankfully, by making the following change to CDSoundEngine’s _getSourceIndexForSourceGroup method, we can solve this problem:

alGetSourcei(_sources[sourceIndex].sourceId, AL_SOURCE_STATE, &sourceState);
if (thisSourceGroup->nonInterruptible) 
{
	// Check if this source is playing, if so it can't be interrupted
	if (sourceState != AL_PLAYING && sourceState != AL_PAUSED)
	{
		//Set start index so next search starts at the next position
		thisSourceGroup->startIndex = thisSourceGroup->currentIndex + 1;
		break;
	} 
	else 
	{
		sourceIndex = -1; //The source index was no good because the source was playing
	}	
} 
else 
{	
	if(sourceState != AL_PAUSED) 
	{
		// Set start index so next search starts at the next position
		thisSourceGroup->startIndex = thisSourceGroup->currentIndex + 1;
		break;
	}
}

And that’s how I added sound effect pause and resume support to my cocos2d title Lumicon. Hopefully it helps developers in a similar situation.