I was working on some home project the other day and I needed some images for
a simple 2d game. Once I had made all the graphics in Photoshop I had to save
each one to a separate file, then flatten each of those files, copy and paste
each one
back in to the original, then align the flattened copy with its corresponding part in
the original and then look in the info palette and write down its coordinates.
Just 10 images ended up taking 45 mins.
I
thought there had to be a better way. Photoshop has had the ability to be
scripted externally for a while now but it only actually got truly useful as of
version 8.0 or Photoshop CS. I looked into it and the result is this script that
will automatically cut out all my images and save the relevant information to a
file ready for use in a game.
Imagine you are going to make a simple 2d game called Crowhunt. You need the
following graphics. A crow, a feather, a crosshair, a hand + gun, the title
graphics and a background image.
The script I wrote works by going down the first level of layers and groups
in Photoshop and turning each one into a separate file cropped to the size of
that layer.
It saves the images by the name of that layer or group.
One other feature I needed was the ability to mark a particular spot in an
image as a reference point, usually as a rotation point. Before I would put the
Photoshop cursor over that point, look at the info palette and write it down but
again, that was a pain in the ass so instead I now create a group, name the
group what I want the result saved as, put the layer with the image I want inside the group and
then create a new layer inside that group with the name "rotpoint". In that
layer I put a single pixel using the pencil tool, any color. The script will
save the location of that pixel for me. You can see to the left in my example
layer palette the handgun group has a "rotpoint" layer and if you look
closely above you can see the 1 pixel yellow dot in the sleeve of the arm. That
pixel will not appear in the final file, it just marks the rotation spot.
I also did not want to have to flatten anything and I wanted to be able to
use Photoshop's layer effects. If you are a Photoshop user you might notice on
the layers on the left the crosshair is a vector based layer and the title is a
text based layer and the both have layer effects applied. The script will keep
them as you see them there.
Once it's done I save the Photoshop file and run the script. When it's done
I'll have one Photoshop file for each layer or group I had in the original,
trimmed to its minimum size.
Here are the images as cut from the sample above
The first thing to notice is the handgun has more graphics than it appeared
to originally. When you paste something into a layer in Photoshop, Photoshop by
default keeps all of it. Later you might decide to move that layer and if you
dragged it further into the image you'd be glad that's the way it works. In my
case though I just wanted exactly what was on the screen so I did a
"Select->All" and an "Image->Crop". Because I had all selected it doesn't appear
the crop did anything but it actually cropped the stuff from any layer that had
parts outside the image. I ran the script again and this time the handgun came
out as I needed it.
The script also wrote out this file.
#define CUT_ALLPARTS \
SPRITEINFO(0,HANDGUN,"handgun.psd",120,285,121,286,70,214,184,300) \
SPRITEINFO(1,CROSSHAIR,"crosshair.psd",178,114,348,284,178,114,348,284) \
SPRITEINFO(2,TITLE,"title.psd",15,15,389,112,15,15,389,112) \
SPRITEINFO(3,CROW,"crow.psd",52,101,139,247,52,101,139,247) \
SPRITEINFO(4,FEATHER,"feather.psd",160,128,171,183,160,128,171,183) \
SPRITEINFO(5,BACKGROUND,"background.psd",0,0,400,300,0,0,400,300) \
The values in the macros are as follows. The first number is an index
if you want one, followed by an ID and then a filename. The next 4 values are the
bounds of the rotpoint or the bounds of the image if no rotpoint existed. The
second set are the bounds of the image. The format of those are left, top,
right, bottom where right and bottom are 1 pixel off the right and bottom. In
other words, the width of an image is (right - left) not (right - left + 1).
To use that info in a game in C++ you might do something like this
#undef SPRITEINFO
#include "cut_allparts.psd.info.cpp"
#undef SPRITEINFO
#define
SPRITEINFO(ndx,id,fname,rotleft,rottop,rotright,rotbtm,left,top,right,btm) \
IMAGEID_ ## id,
enum ImageIds
{
CUT_ALLPARTS
IMAGEID_last,
};
struct ImageInfo
{
const char* filename;
int xPos;
int yPos;
int width;
int height;
int centerX;
int centerY;
};
#undef SPRITEINFO
#define SPRITEINFO( \
id, \
label, \
filename, \
centerLeft, \
centerTop, \
centerRight, \
centerBottom, \
cutLeft, \
cutTop, \
cutRight, \
cutBottom) \
{ _T("media/images/") _T(filename), \
(centerRight + centerLeft) / 2, \
(centerBottom + centerTop) / 2, \
cutRight - cutLeft, \
cutBottom - cutTop, \
(centerRight + centerLeft) / 2 - cutLeft, \
(centerBottom + centerTop) / 2 - cutTop, \
},
ImageInfo ImageTable[] =
{
CUT_ALLPARTS
};
Unfortunately, I know very few engines that can directly load Photoshop files. For my purposes I needed targa (.TGA) files. While it *should* be possible to get Photoshop to save targas the problem is I wanted alpha channels as well and I wanted them to have alpha that included the layer effects from
Photoshop.
That's not trivial in Photoshop. The steps are something like:
- create a new empty layer
- merge the visible layers. This will remove the effects and bake them into
the image.
- select the transparency of the layer
- set the background color to black and the foreground color to white
- go to the channel palette and create a new channel
- fill the selection with white
You'd now have the correct alpha. Unfortunately those steps are not easily
accessible from scripting in Photoshop. While it would be possible, under the
current script engine it requires using actions. I tried it once, Photoshop
comes with a "ScriptListener" which will save all everything you do in Photoshop
to a script which you can then edit later. I tried it but it was not easy to
understand and as I already had a different solution that worked I decided to
use that instead.
My different solution is a tool that is part of the
libraries I use for games development written back in 1995
that will load Photoshop files and save them as
targas (among other things). Photoshop by default saves a preview image inside
its files that is the result off all the layers together as well as the
transparency so converting that to a targa through my old tools gives me exactly
what I need.
Here you can see the result. You can see the glow around the title and the
shadow and border around the crosshair as well as the correct alpha for each of
them.
To use the Photoshop cutsprite script manually you'll need at least Photoshop
CS. Make a Photoshop file following the rules described above with each image in
a layer or group and give a name to each layer or group. Save it. Then, Pick
File->Scripts->Browse... from the menus and choose openandcutsprites.jsx.
An open file dialog will appear. Select the file you saved. The script will then
follow these steps
- load the file
- separate each layer and group to a new file and save it to the same folder
as the original based on the name of the layer or group.
- write out a <filename>.info.cpp file with in info about the sizes of the
images.
- close the original image.
Warning: There is NO check for overwriting files so be
careful!
I also wrote a script that will go through a tree of folders finding any Photoshop file that begins with "cut_" and will cut that file automatically. To run that you'll need to install
ActivePerl (free). I could have written
the script in VBScript or JScript but I'm far more comfortable with Perl so I
did it there. Perl supports Microsoft's "Windows
Scripting Host" which is basically a way for any language to be used as a
scripting language for Windows. Microsoft supplies VBScript and JScript. There
is also PerlScript and PythonScript.
The script will start from the current folder and look in every subfolder for
any file that starts with "cut_" and ends in ".psd". If it sees such a file it
will tell Photoshop to run the script above on that file. The easiest way to use
it is just to copy it and the rest of the files to the base folder of whatever
project you are working on. Double click on cutallsprites.wsf and it
should run. Oh, before you do that you should open a command line and type
cscript //H:CScript
capitalization is important! This will set Windows Script Host to default to
using the command line version.
Also included is another Perl program called processPSDtoTGA.pl. This program
will walk all the folders from the current folder down and any ".PSD" file it
finds it will create a corresponding ".TGA" file. Like the previous tool it's
easiest if you just copy this and all the other files to the base folder of your
current project and double click it to run it.
I hope you find this useful. The code is released under a BSD license so feel free to modify any of these to
suit your own needs. You can download it from the link below
cutsprites.1.4.zip
(666k)
Maya Support
I added a Maya Ascii scene file generator to the cutallsprites.wsf
script so once the Photoshop file has been cut you'll find a ".ma" maya ascii file there. Load it up in Maya and it should reproduce the
original setup along with points of origin in the correct spots.
Often many 3d engines have limited 2d abilities. They'll have a full on
animated textures, materials, translation, rotation and scale for 3d but nothing
for 2d. Well, just setup an orthographic camera and use your 3d engine for your
2d. Much simpler than writing a completely new system.
Version History:
- 1.3 - 2006/03/10
- Added "bg_color" layer support. Layers with the name "bg_color" will be turned on if they exist. You can put them inside groups to make them apply to only one group and you can have more than 1.
What's the point?
Well, for many 3D hardware if you want to display a colored texture with transparency you basically need to draw in full color in the color layer and just set the alpha for transparency. In other words, lets say you wanted to have the word "Start" in red. If you just draw it in red with no "bg_color" layer you will get a texture with red pixels on a white background and the correct alpha channel. The problem is the pixels that are anti-aliased at the edges already have white mixed in them. If you use this on some 3D hardware your word will have a slighty white border. To fix this create a layer, name it "bg_color" and fill it with red. Since it's probably confusing hide the layer. Now run the script and you'll get a completely red texture but with the correct alpha. This will look correct on hardware that had issue with the previous texture. See the included sample "cut_bgexample.psd".
- Added support for global "cropsize" layers. Now you can put multiple layers and crop them all to the same size. See the included sample "cut_bgexample.psd".
- Added forcing to a even number of pixels. This should probably be an option since your tools might not have an even pixel limit but 1 extra pixels is not going to bother most people so I decided not to add an option. If you don't like it edit the code.
- 1.2 - 2006/03/04
- Added maya ascii scene generator
- Moved targa generation to photoshop script
- Added black blending / premult for alpha if targa
- 1.1 - 2006/02/17
- Similar to the "rotpoint" layer, added a check for "cropsize" inside a group.
If found the group will be cropped to the size of the "cropsize" layer.