Multiplayer Network Solutions for Unity (Photon and PlayFab)

Aside from working on a side project to my side project, which I recently finished, I’ve gotten back to my original side project (my next game) and found myself researching on some tech for a large feature. Talk about “writing in code”! Spit it out! That tech I’ve been researching on is multiplayer networking solutions for Unity. There. Like the title didn’t give it away.

For the uninitiated, multiplayer games are games that can be played with other human beings across the Internet. Yes, this includes social games like those popular Facebook games of old*, and games like Pokemon GO, all the way to games like Call of Duty and World of Warcraft. Some of these games have competitive play and others have cooperative play, and sometimes there’s a combination of both. This, of course then, is different from single player games, like Crossy Road or Flappy Bird, which don’t involve multiple people playing together. Some games actually have both singleplayer and multiplayer modes, like the aforementioned Call of Duty. Nonetheless, I’ve already established that I’m writing about multiplayer here.

Game developers know full well that one of the most difficult systems to build is the multiplayer networking system of a game. I’m not even going to go into how difficult it is to build a Massively-Multiplayer Online (MMO) game, as that’s way out of scope of this article. If you’re still thinking about it, I’d highly recommend reconsidering your life goals.

But enough of the intro.

Unity Networking, Photon SDK, and PUN

For the past several days, I’ve been looking at potential network solutions for Unity, namely Unity Networking (Unity’s first-party solution) and Photon by Exit Games. From what I gathered, Unity Networking still needs some time to mature, whereas Photon is a more established networking package, with a long running track record. I decided to go with Photon for this reason, as well as for its healthy amount of documentation, so I’ll go into more detail about it here.

As of this writing, Photon has a sort of mixed personality. For Unity, it comes in two flavors. Photon SDK for Unity, and Photon Unity Networking. Whazzawha? Right. Photon SDK for Unity is available on the Exit Games website. Whereas Photon Unity Networking (PUN) is available as a downloadable asset package from the Unity asset store. The difference is that PUN is a wrapper around the underlying Photon SDK, to help you save time with the lower level groundwork code already implemented for you. PUN is also supposed to emulate the API of Unity Networking, so that it is more familiar to the Unity network application programmer.

So, I chose to use the Photon SDK for Unity. Why would that be if PUN already provides a lot of the underlying work for me? Well, there’s one feature that is currently not supported by PUN that I need for my next game: Persistence of asynchronous game sessions. I’ll go into more detail about what that is in a moment.

To make things more complicated, doing any Google searches for Photon Networking will inevitably yield results on “Realtime” vs “Turnbased” Photon. The hope for Exit Games was that Photon can be marketed for developing games that are fast-paced, and played simultaneously, or synchronously, between gamers (Realtime), and for games that are slower-paced, and played by one player at a time, or asynchronously, like Chess (Turn-Based). As of May 2016, Exit Games discontinued Photon Turnbased, which by itself sounds as if Photon would no longer support turn-based games. In reality, the Turnbased features have been merged into Photon Realtime. So that means, I’m using Photon RealTime for a turn-based game. How confusing! At the same time that Exit Games was attempting to simplify the delivery of their product(s) to its customers, it managed to confuse even newer customers like me.

Asynchronous Gameplay and Persistence

To support my next game, which is a turn-based game, I need to implement asynchronous gameplay and persistence via something called Webhooks/WebRPCs.

“Asynchronous gameplay” means that the players do not play simultaneously, together on the same turn. From a network programming perspective, this is far less difficult to implement than synchronous gameplay because with synchronous, realtime gameplay, there is no concept of “turns”. With synchronous games, developers have to implement the system such that every player connected to the game can give input and receive a result immediately, so as to produce the illusion that all players “exist” in the same virtual world. In reality, it takes fractions of a second, or even whole seconds to transmit that data across to all other connected players. Network programmers have to implement numerous tricks and techniques like dead reckoning and client prediction to generate the illusion that a player has moved, without any lag in time. Otherwise, if the game goes out of sync, the illusion is completely broken.

For each turn that a player takes in my turn-based game, the current state of the game has to be saved so that the next player can continue from that state. This is where persistence comes in.

So for persistence, or storage, of game sessions in progress, the Photon SDK provides Webhooks and WebRPCs to load and save game data; something that PUN does not provide.

Webhooks and WebRPCs go hand-in-hand for game clients to communicate with web servers. WebRPCs (Remote Procedure Calls) are used by clients to send messages across the network, so that web hooks on a web server can receive those messages and process them in a secure, authenticated environment. Note that web servers are your typical type of server for hosting web pages. So while in this case, a web server is being used as a game server, it could also be the host for web pages to convey that data, say, for players to check their progress, or the states of their game sessions. Also note that web servers are not used as game servers for real-time games. Servers for real-time games require support for higher loads and bandwidth, because they generally process a lot more network traffic than for turn-based games, to maintain synchronicity across all connected clients, as mentioned above. This is why web servers are more suitable for turn-based games.

And with all of that knowledge, I discovered that Photon does not provide web servers, only the hooks! Bah! Before learning about it, I thought Photon was going to be my one-stop shop for all my network and multiplayer needs for my game. At least, that’s what their marketing made me feel like what it was. In reality, a more seasoned network gameplay engineer probably would have been able to see past the bullet points and tell me that I still needed another component to get Photon working the way I wanted for my turn-based game.

Luckily, through Photon documentation (or somewhere on the Photon or Unity forums), I found out that PlayFab is the recommended solution that provides that web server that I’m looking for. Not only that, it provides some other good stuff that I’d need that Photon doesn’t provide, like user accounts, leaderboards, etc. Also, GameSparks is a competitor which provides pretty much the same thing, but I admit that I didn’t do much research into which would be better for me. Since PlayFab has a partnership with Photon, I decided to go with that.

PlayFab

Once I found out that I had to use yet another system besides Photon, my heart sunk. I was already feeling pretty overwhelmed with having to learn the new Photon API. I was reluctant to learn even more than I was planning to. But we’re game developers, right? We keep learning and work through it! That’s what we do!

Turns out PlayFab, after learning what they offer and how to navigate their browser-based Game Manager, provides quite a bit for the entry price of free. I like how it provides user accounts, tracks when and where they’ve logged on, and other analytics, such as segmentation, which is a business term that is beyond the scope of this post. They have this system called PlayStream which is intended for logging, but I haven’t figured this one out yet. What I was most interested in was, of course, how to implement web hooks for the Photon service.

PlayFab has a proprietary scripting system called CloudScript, found in the Automation section of the GameManager, which is essentially Javascript functions on the web server that handle the events sent via Photon from your game client. I find it a bit awkward and time consuming to upload and deploy cloudscript to PlayFab, but since it’s free, and they’re providing the web servers for me, I’m not complaining all that much. At least not yet… Well (that didn’t take long), trudging through more documentation on PlayFab, I discovered that the free tier of the service is severely limiting in terms of how much data you can save to their web server. It’s understandable, as it’s free, but it’s a fair warning to anyone who wants to try it out, that once you roll a game into production and release, you’ll most likely need to choose a non-free tier to get all your data persisted on their servers. 

Keep On Keepin’ On

At the time I am writing this, I’m still in the midst of working through Photon and PlayFab so that I can get a working game flow for the player. While there’s a wealth of information on both services, it’s very fragmented and difficult to piece everything together. In future posts, I’d like to go more in-depth with my Photon and PlayFab setup, because I’m finding it a pain to get through right now. Perhaps others planning to take this route will benefit from my findings. In the mean time…

Make it fun!

 

 

* Of course Facebook is not that old. But in “technology-and-video-game” years, 4-5 years is like 4-5 decades…. Is Farmville even still a thing?

Posted in Dev, Programming | Leave a comment

Time-lapse Gamedev Session Recording

Here are a few tips for some of you indie game developers out there, that are looking to show off some of your work via time-lapse video.

CamStudio

Download CamStudio, which is a free and open source video capture program.

Use the following settings:
Options > Video Options... > Compressor: Cinepak (default)

Uncheck Auto Adjust at the bottom. This will allow you to adjust the Key Frame frequency, and the Framerates.  I set Set Key Frames Every 1 frame(s)

And for framerates,
Capture Frames Every 1000 milliseconds
Playback Rate at 2 frames/second


This means, that while recording, CamStudio will capture the screen every 1 second, and it will play back twice as fast.
After watching a recording at this playback speed, it was still a little too slow, but most video players nowadays have a speed control, so I wasn’t too worried about it, especially if I decide to export these time-lapse videos to animated GIFs (more on this later), I would have the opportunity to set the speed prior to export.

Another setting that I like to enable is even pixels (which for whatever reason I don’t see in v2.7.3). This is just to allow more video players to run the resulting video, as an odd number of pixels tends to result in encodings that aren’t really friendly to some video players.

Be sure to set your recording region and output directory accordingly, and you’re ready to rock.

When you hit Record (the obvious big red button), corner brackets will pulse at the edges of the recording region, at the specified Capture Frames Every setting, so for example, if I was using 1000 milliseconds, I would see the brackets pulse every second. What’s great about this is, for time-lapse recording, CamStudio doesn’t eat up a lot of resources to capture video while you are doing what you are doing.

Animated GIFs

To create an animated GIF, you need a sequence of still images, or frames, that you would then re-assemble into an animated GIF file.

Converting Video to Frames

If you wanted to convert the time-lapse video you just created above into an animated GIF, you’ll have to extract still frames from the video as a sequence of images before assembling the GIF.

I like to use avidemux (another free and/or open source software program). You can save a video, or portion of a video loaded into avidemux, export it as frames. Be sure to install avidemux 2.5.x, as 2.6.x does not have this feature.

Capturing Frames Directly

Alternatively, you can use a program to capture time-lapse still frames, bypassing the video recording stage altogether. Apparently there are a number of applications that create time-lapse video and/or screenshots, but the one I tried was chronolapse after stumbling across this article.

chronolapse has the option of capturing screenshots or images from a webcam, or both. The program outputs a sequence of jpg images that you can later assemble in another program for creating animated GIFs. One program you can use to do this is GIMP. There are far easier-to-use commercial programs to do this entire process, but I wanted to keep the software in this article free and/or open source.

Exporting Frames to Animated GIF with GIMP

Once you have the sequence of still images, you can use GIMP to export the final animated GIF.

To do this, open up GIMP and create a new image with the same dimensions as the exported images.

Then, select all the images in the sequence in an explorer window, and drag them onto the Layers window to load each image as a layer; be sure to drag the first image in the sequence first, or else the sequence may get out of order. This process can take a long time if you have a large number of images and/or the dimensions of your images are large.

Once all the images are loaded into the Layers window, don’t forget to delete the first frame that contains only a blank image from when you originally created the file. Before exporting, you may have to reverse the order of the images in the layers by using Layer > Stack > Reverse Layer Order, because GIMP is weird sometimes. You can then click on File > Export... This will prompt you to choose your directory and the file type (GIF), and then you can click on Export, and it will bring up this dialog:

Select As Animation, and your looping and delay options. Once you click on Export here, it may or may not again take a long time to process, depending on the number of frames and dimensions of your image.

One last thing I should mention is, animated GIFs can be fairly large for the web, depending on the length and dimensions of the animation. webm is another animated image format that is pretty popular online now, is relatively smaller in size to animated GIFs, and you can find some converters with a quick search.

And done!

Here’s an example of what I used these time-lapse techniques for (it’s a fairly large GIF; apologies in advance):

I was 3D modeling the characters Dipper and Mabel Pines from the show Gravity Falls for an eventual 3D printed gift for my significant other. It turns out that a lot of the principles for 3D modeling for game development also apply to 3D printing. Maybe I’ll write up some future articles on the subject…

Here’s a summary of the tools I just mentioned:

Anyway, happy developing!

Make it fun!

Posted in Dev | Leave a comment

Forecast: Stormy

My colleague hired me to do a contract art piece for him. It’s a logo to celebrate the release of the 100th episode of his YouTube video channel.

This is what it looks like:006

Yes, his name is Storm. Yep, Storm. I’m sure he gets that a lot. You know, your reaction; that face that you’re making. His channel is all about games. He does reviews, and they’re pretty damn funny and entertaining and informative.

Anyway, I encourage you to take a break from whatever it is you’re doing, be it developing a game or reading about Under the Weather.

Here’s the ep:

Anyway, that’s enough for the plug.

Make it fun!

Posted in Thoughts | Leave a comment

What’s Happening in the Industry?

So, let’s talk about games. As I write this, Pokémon GO is still going strong, No Man’s Sky has lost 90% of its user base in two weeks, and the Rio Olympics have wrapped up.

I’ll tackle the Pokémon GO craze first.

pokemon_go_logo

I’m playing it. Who isn’t, right? I didn’t plan on playing it, but I got slowly sucked into it. And so now, my better half and I go out hunting some nights. But seriously, what is it about this game? On the surface, it’s just some mobile, social game with AR (Augmented Reality) and GPS (Global Positioning System) features. But those are just some of  the technical components of the game that form the whole. The magic sprinkle on top is the IP (intellectual property) tie-in, with the Pokémon monsters, right? Well, yes and no.

Pokémon GO’s success did not happen overnight. In this post on Reddit, Niantic CEO John Hanke describes that the game was about two decades in the making. I believe that Pokémon GO’s success goes much deeper than the integrated IP, and the use of cameras and GPS, and even beyond the established database of information used for the game. As a piece of software in general, it’s a finely crafted, well designed game. It contains many standard staples of RPG (role-playing game) mechanics, such as XP (experience points), and several other forms of currency, like “stardust”, “candy”, “coins”, as well as many statistics, like combat power, and size and weight, for each monster that you catch. You can evolve Pokémon into more powerful forms, and you can hatch them from eggs, which adds an element of randomization, anticipation, and surprise to the game.

All of these game design elements combine to make an overall cohesive experience, from the moment-to-moment gameplay of throwing curveballs to gain extra points, all the way to the metagame of territorial competition at the various gyms across the map. I realize that some of these concepts are not foreign to existing Pokémon fans who have played previous games based on the IP, or have watched the animated series. This is all new to me, as I was never into Pokémon , and I barely knew any of them beyond the classic Pikachu, Charmander, Bulbasaur, and Squirtle.

gold_rush

Naturally, the game development community immediately jumped on any clues, ideas, and research on how to make a Pokémon GO clone, so that those opportunistic types can cash in on the trend, while the more clever developers would pump out tutorials and resources to easily plug into your own game to get you along your way; providing the shovels and blue jeans, so to speak. But if Pokémon GO took well over a decade to slowly build its data infrastructure, what makes people think that they can emulate Niantic’s success? As I mentioned earlier, the individual technological features of the game is not what makes the game stand out. For example, the AR component is still considered a gimmick, as the more seasoned Pokémon GO players turn this feature off, because the non-AR view gives them more control while trying to capture those elusive creatures.

So from a developer’s perspective, a game like Pokémon GO can never become an overnight success. There is just too much technological infrastructure and experience employed to make the game as polished as it is, server woes notwithstanding. Another example of this phenomenon is the story behind Rovio, the company that developed the game, Angry Birds. Rovio overcame many failures, before reaching their success on their smash hit.

And now, you’re lucky to find a piece of merchandise that doesn’t have an Angry Birds character on it! Anyway, I can probably keep going on about Pokémon GO, as there’s plenty to analyze about the game, from different angles of game development. So I’ll quit it here for now.

On the flip side, there’s a game called No Man’s Sky.

no_mans_sky_logo

To summarize, No Man’s Sky is a game that was recently released after several years of having generated hype, so there was plenty of anticipation for this game. Many players are upset about what the product was on the first day of release, and what it is today. It turns out, the game did not live up to its high expectations, as it was released with many missing features that the developers “promised”. After two weeks on the market, the game’s user base has dropped off dramatically.

You see, one of the many challenges, if not the biggest challenge, for independent game developers, is marketing their games. They’d rather be building, creating their game than spending time telling people about it. And then, when it comes time to launch, the game ends up flopping, or at the very least, not reaching the audience that it is intended for. And that’s why marketing is such a huge piece of the puzzle. The video game industry, especially the independent game industry, is no longer in a state of an “if they build it, they will come” mentality. Perhaps that was the case during the Braid and Limbo “era”, but now,  the tools are readily accessible at little to no cost. All an aspiring game developer needs to get started is to “level up his/her skills, put in a lot of elbow grease, and give it plenty of time to bake.” But in order to increase the chance of success, you have to throw in as much time, effort, and basically, blood, sweat and tears into making a product that you think people will like (more on this another time), and then half the battle is done. Just half?!? Yes, just half. Marketing is that other half, and that means communicating to the outside world; screaming at the top of your lungs, that you have a product that you’d like to share. Simply put, the more people who know about your game, the more the percentage of people that will actually try your game.

Anyway, off of the tangent… No Man’s Sky was marketed EXTREMELY well. The hype train was coming along strongly, which is a good thing… but only for a good, complete game. Without any substance to back up the marketing, No Man’s Sky fizzled out. It lacked any staying power. Typically, games can last several weeks to months, and some rare successful games have mass followings that will make it last for years after release, as in the case of games like World of Warcraft and EVE Online. Instead, No Man’s Sky saw a lot of players request refunds from the various distributors of the game.

Among all the rare successes in the game industry, No Man’s Sky represents yet another failure of game development, and software development in general. Godus is a game by renowned game designer Peter Molyneux, created by his independent studio 22 Cans. The game’s notoriety grew as the direction of development ran askew, and like No Man’s Sky, promised rewards didn’t get delivered. The game was initially funded by a KickStarter campaign, and the company over-promised and under-delivered. The story, written by Nathan Grayson over at Kotaku, is intriguing if you have the time to read it. Some other notable failures are the Batman game Arkham Knight released on PC and Colonial Marines, a game based on the popular Alien/Aliens movie franchise. Both games had high hopes, only to be riddled with bugs and performance issues, and in addition to that, lack of content, in the case of Colonial Marines.

So well, this is a quick look at the most popular trends in the video game industry in 2016, at least, as of the end of Summer. Typically, the industry has a lot more cooking around the Winter holidays, for the obvious reason that kids are looking to get electronic gifts from their parents Santa that are not Tiger handhelds.

As far as the Olympics goes, well, I hadn’t paid much attention to it, other than to all the devastating stories about Zika, green pool water, pollution, and a disgraceful “bro” story. You can go look those up for yourself, because some of them are too disgusting to even write about. I’m glad it’s all over though, so we can get back to regularly scheduled programming.

Well, that’s about all I feel that I want to write about right now. I’ve been behind the scenes, hard at work on my next game, but I’m still not ready to reveal anything yet.

Until next time,

Make it fun!

 

Posted in Industry, Thoughts | Tagged , | Leave a comment

Plotting a New Course

Under the Weather is taking a change in direction for the foreseeable future. I started working at a great company that is completely unrelated to games, which means I’ll be scaling back the time I spend on Under the Weather games, websites, and social media. Essentially, the projects I work on for Under the Weather are now relegated back to “hobby” status.

The Reasons

Anyone’s first thought into why I would make this decision can be that I am running out of money to continue funding a startup. While I am not immediately seeing the return on investment within the first 12 months of effort, money was not one of the more important reasons for the change. Don’t get me wrong. I’m not rich, but I wasn’t going to start panhandling either.

If anything, it was a personal decision. Life happens, and some events in life take higher priority than others. I don’t really want to go into more detail than that.

The Strategies

Making a conscious decision to look for a job required a significant mindset change. I was already in the midst of developing my next game (and I still am, but at a lesser capacity now), and then I had to update the resume, brush up on those academic interview questions, and generally get ready to get back to working for a company that’s not my own. I found a few prospective places that I could work for, but luckily I found a software engineer position at a robotics company whose products are for the scientific community. So yeah. For science!

The place I’m working for allows me the amount of creative freedom on my off time that working at a game studio does not. One of the drawbacks I had while working at a game company is that I felt creatively starved.  For one thing, working on your own game on your personal time is considered a conflict of interest. For another, working at a game studio tends to use so much of your creative energy, that you don’t really have any left after going home for the day. I have to say though, that working in the game industry is a great experience, but during crunch time, when you absolutely need to ship the game, it can get incredibly stressful and draining.

The Future

So, with my free time a lot more limited now, I have several avenues I wanted to explore for developing and growing Under the Weather. I’m not ready to reveal anything yet, but at some point, I’d love to divulge more details about the first year of development of the company and products. There are plenty of game development stories out there, and for some reason, I personally can’t get enough of them, so this time, it’s my turn to contribute. This probably also means that, perhaps now, this blog will be a little bit more free form, in the sense that, before I started this whole Under the Weather thing, I had no idea how to blog, and how I should blog for a company. Well, this is me. I’ll write about stuff. Games, software, and things related. So for my next post, I’ll probably get to talking about Pokémon GO…

 

Make it fun.

Posted in Announcements | 2 Comments

How to Extract Layers from a Photoshop File with ImageMagick and Python

For a project that I’m working on, I need a way to extract each layer of a Photoshop PSD file and have them written out as separate PNG image files. To do this, my first thought was to use ImageMagick. ImageMagick is a graphics library used to manipulate images and convert them to different graphic file types, among many other things. I’ve used ImageMagick before, but I don’t use it enough to remember all of the syntax by heart, so I had to do a lot of google searches to compile this solution together. I could have rolled my own solution, but time is the enemy, so using any sort of libraries that saves you hours, days, or weeks, is an obvious win.

Requirements

The requirements for this system are pretty simple. A Python script can be run from the command line, given the path to a Photoshop PSD file. After the script is complete, the folder containing the PSD file now also contains all PNG files for each layer in the PSD file.

Overview of Extracting Layers from Photoshop with ImageMagick and Python

Overview of Extracting Layers from Photoshop with ImageMagick and Python

Pretty simple, right? Not quite.

As I was implementing this solution, I discovered some information and processing techniques that aren’t immediately available through ImageMagick’s commands.

The most basic command to extract layers from a PSD file is straightforward enough:

convert <filename>.psd[1] <extracted-filename>.png

Where, the number 1 is the index of a layer in the PSD file. But here are the problems I ran into:

Irregular Image Sizes for Alpha Layers

If the layer is an alpha layer, all of the transparent pixels on the edge of the image on that layer are not accounted for in the dimension of the output file, thereby resulting in images with varying dimensions. For example:

Irregular Alpha Images when Extracting Layers from Photoshop with ImageMagick

Irregular Alpha Images when Extracting Layers from Photoshop with ImageMagick

This is a no-go for my purposes, because I need to later re-composite the layers, and if each output layer is a different size, they can’t be easily merged back together.

Unable to Export by Layer Name

This is another feature that I was expecting to use in ImageMagick that isn’t actually there.

To export a layer, you have to use the index rather than the name of the layer. I suppose this change isn’t absolutely necessary as a requirement for everyone, but I feel that it’s a lot more convenient to specify the name of a layer that’s easily cross-referenced by human eyes in the Photoshop file, as opposed to having to count what index is required in the layer stack. Not only that, you can do other selection tricks, like filter layers by specific names, which is actually the real purpose for supporting this.

So those are the two major problems I ran into, and that’s the reason why I chose Python for this solution.The solution below could very well have been done using batch scripts, or some other scripting language, but I’m a Python user. There are multiple steps to get this working. In the Implementation section, I’ll spill out all the details.

Setup

Before I dive into the details, I want to mention that this article already assumes that you have your environment set up to run these operations. If you’re sure that it’s set up, feel free to skip ahead to the Implementation section.

Photoshop – I use Photoshop Elements, but you’ll obviously need some program to author PSD files for processing.

ImageMagick – I initially started implementing this with version 6.9.3, but have since upgraded to 7.0.1. I was taken aback a bit, since by default, versions 7.0.+ have marked the convert command as legacy, and the solution in this blog post uses the convert command. If you are using this version, be sure to check the option to “Install legacy utilities (e.g. convert)” before installing it. Older versions do not have this problem.

Python – I’m using the 2 series, not the 3 series for this solution. Be sure that you can run Python files directly from anywhere in the file system, without having to specify python.exe in front of it. For me, I had to do 'regedit', and modify the value in HKEY_CLASSES_ROOT/py_auto_file/shell/open/command to:

"C:\Python27\python.exe" "%1" %*

Moving along…

Implementation

Fixing the Alpha Image Size Problem

Let’s solve the problem with the irregular alpha image sizes first. I stumbled across the answer on this thread here. But I’ll explain it a little further here, as the syntax is a little strange.

convert <filename>.psd[0] <filename>.psd[2] ( -clone 0 -alpha transparent ) -swap 0 +delete -coalesce -compose src-over -composite <extracted-filename>.png

The first two parameters are the first two image files in the “sequence” for ImageMagick to process. The index (i.e. [0] and [2]) indicates the layer to process. Layer 0 is not really one of the layers in the PSD, but it’s actually the flattened image of all layers in the PSD. Layer 2 is the second layer in the PSD, for this example.

The part of the command in parentheses are options processed as a unit, before moving on to the rest of the options. It creates a clone of the first image in the sequence, and then wipes it clear as an alpha image that’s completely transparent. This new image is tacked on as the third image in the sequence.

The next option (-swap) swaps the last image with the first image in the sequence, and the +delete option deletes the last image (which used to be the first image) because we no longer need it.

I’m not actually sure why -coalesce is needed here, but -compose src-over sets up the image for alpha compositing such that the overlaying image is blended according to the transparency in that layer. The -composite option then performs the compositing of the two images, and outputs the file as <extracted-filename>.png.

Whew! That took a lot to explain, but I hope it at least gives an idea of how ImageMagick is processing the layers in the PSD to produce the resulting images.

I realize that there may be other solutions to this problem, but this is the one I found that actually worked for me. If you have a solution that is more elegant, feel free to ping me, and I can add it here.

Exporting Layers by Name

We handle exporting PSD layers by name in two parts. The first part is getting the layer names, and the second part is using those names. All of the code for this part uses Python.

Part I – Getting the Layer Names

Here is the file in its entirety. We’ll discuss the major points about the code after you take a gander.

Download

#  PSDLayerInfo

# Copyright (c) 2016 Under the Weather, LLC
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software
# and associated documentation files (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all copies
# or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
# FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

import os
import sys
import subprocess
import string


class PSDLayerInfo:

    def __init__(self):
        self.layer_names = []

    def process(self, argv):

        if len(argv) < 2:
            print('No file specified')
            return;
        
        inputFile = argv[1]
        # imagemagick command to print out information about the input file
        identify_cmd = 'identify -verbose ' + inputFile

        # check_output runs the specified command, and writes the result string to the var
        psdInfo = subprocess.check_output(identify_cmd, shell=True)

        # copy the info to be consumed
        data = psdInfo
        while data:
            data = self.find_layer_name(data)

        for name in self.layer_names:
            print(name)

    # partitions the string based on the "label: " string
    # collects the names in layer_names
    def find_layer_name(self, inputStr):
        parts = inputStr.partition("label: ")
        lastPart = parts[2]
        if lastPart:
            index = lastPart.find('\n')
            if (index is not -1):
                self.layer_names.append(lastPart[:index])

        return lastPart
    
def main():
    argv = sys.argv
    layerInfo = PSDLayerInfo()
    layerInfo.process(argv)
    

if __name__ == "__main__":
    main()

So, this script accepts the name of a PSD file as a parameter.

The first notable item is this line:

identify_cmd = 'identify -verbose ' + inputFile

identify is an ImageMagick command, and we are using it here to get some information about the PSD file; specifically, the layer names.

psdInfo = subprocess.check_output(identify_cmd, shell=True)

subprocess.check_output() is a Python function that runs an external program and pipes the output from that function into a byte array, psdInfo.

psdInfo contains tons of information about the PSD file. The function find_layer_name() takes that string, iteratively parses it, and collects all the layer names identified by every occurrence of the "label: " string in psdInfo.

def find_layer_name(self, inputStr):
   parts = inputStr.partition("label: ")
   lastPart = parts[2]
   if lastPart:
      index = lastPart.find('\n')
      if (index is not -1):
         self.layer_names.append(lastPart[:index])

   return lastPart

The partition() function separates the input string and outputs a 3-tuple as 3 string objects, the delimiter "label: " being the second string object. Since we're only interested in the stuff after the "label: " delimiter, we always look at parts[2], being the last part of the string.

Once we have the layer names, we simply print them to the output.

Part II - Using the Layer Names

So, now that we have a way to collect all the layer names from the PSD file, we take those layer names, and export each layer individually, using the convert command that we used to solve the irregular alpha image size problem above.

Download

#  PSDLayerExporter

# Copyright (c) 2016 Under the Weather, LLC
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software
# and associated documentation files (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all copies
# or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
# FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

import os
import sys
import subprocess
import string

class PSDLayerExporter:
    def process(self, argv):
        print("Layer exporter")

        if len(argv) < 2:
            print('No file specified.')
            return

        self.inputFile = argv[1]

        out = subprocess.check_output('PSDLayerInfo.py ' + self.inputFile, shell=True)

        layers = out.split('\n')

        index = 0

        for layer in layers:
            index += 1   
            if layer and (layer.find("base") is not -1 or layer.find("detail") is not -1):
                print("layer: " + layer)
                self.export_layer(index, layer)

    def export_layer(self, psdIndex, layer_name):

        extractedFilename = ""
        extIndex = self.inputFile.rfind(".psd")
        if extIndex is not -1:
            extractedFilename = self.inputFile[:extIndex]

        extractedFilename += "_" + layer_name + ".png"
        cmd = self.inputFile + "[0] " + self.inputFile + "[" + str(psdIndex) + "] ( -clone 0 -alpha transparent ) -swap 0 +delete -coalesce -compose src-over -composite " + extractedFilename;

        commandStr = 'convert ' + cmd

        subprocess.call(commandStr, shell=True)
        
def main():
    argv = sys.argv
    layer_exporter = PSDLayerExporter()
    layer_exporter.process(argv)
    

if __name__ == "__main__":
    main()

For the most part, this file uses common Python syntax and methodology, so I'll again, just point out the major points of interest.

out = subprocess.check_output('PSDLayerInfo.py ' + self.inputFile, shell=True)

Oh look, it's our old friend subprocess.check_output()! We used this function in the other script, to get the output from the ImageMagick identify command. This time, however, we're using it on the PSDLayerInfo.py script that we just discussed above! And this time, we know that the output will be a endline-separated string of all the layer names in the PSD file. Handy!

def export_layer(self, psdIndex, layer_name):

    extractedFilename = ""
    extIndex = self.inputFile.rfind(".psd")
    if extIndex is not -1:
        extractedFilename = self.inputFile[:extIndex]

    extractedFilename += "_" + layer_name + ".png"
    cmd = self.inputFile + "[0] " + self.inputFile + "[" + str(psdIndex) + "] ( -clone 0 -alpha transparent ) -swap 0 +delete -coalesce -compose src-over -composite " + extractedFilename;

    commandStr = 'convert ' + cmd

    subprocess.call(commandStr, shell=True)

export_layer() does most of the work in this file. The first block simply takes the PSD's filename, and modifies it to append the layer name and PNG extension, resulting in the extractedFilename. With that, we call the convert function as mentioned above. Since we don't need any output from this operation, we just call subprocess.call().

 for layer in layers:
     index += 1 
     if layer and (layer.find("base") is not -1 or layer.find("detail") is not -1):
         print("layer: " + layer)
         self.export_layer(index, layer)

Last thing to note here, is that we are looping over all layer names, but only exporting the layers with either "base" or "detail" in their name. This is what I mentioned earlier, about being able to filter out exported layers based on the names of the layers in the PSD. It's not necessary, especially if you know absolutely how many layers and in what order you want your PSD asset files to be in, but it definitely makes it more convenient.

Conclusion

So that wraps it up! Automated PSD layer extraction from now until.... forever! Or more realistically, until Python, ImageMagick, and/or the PSD file format are deprecated, which is probably not any time soon, or even within the next decade or so. But if that is ever the case, well, hopefully I'll have another solution by then. Heck, there might already be another solution that's more elegant and runs faster now, but I do not yet know about it. For now, this works for me. Until next time...

Make it fun!

 

 

  • Adobe Photoshop Elements 9 (for authoring PSDs)
  • Python 2.7.10
  • ImageMagick 7.0.1 (with "convert" command installed)
Posted in Dev, Programming | Leave a comment

Number Crunchers Now Available on iOS

That’s right! Number Crunchers, which was only available for Android for the past several months is now available for the iPhone and iPad!

Play a classic numbers game with a new look and feel! Get it here on the App Store today.

iOS Download

muncher_apple

 

Posted in Announcements | Leave a comment

Horde Rush Now Available On iOS

I am pleased to announce that Horde Rush is now available on iOS. That’s right! Check it out on the App Store here. Play the game on your iPhone or iPad today!

Posted in Announcements | Leave a comment

Ramping up to Take on 2016

Happy 2016! I’ve been off the grid since before Star Wars released. It wasn’t because of Star Wars though, as I had just seen the movie this past weekend. I was keeping busy with everything Holiday celebrations, but mainly have been trying to kick an illness that’s been affecting my productivity.

As for Horde Rush,  I will be submitting the iOS build to Apple sometime soon, but it depends on the results of my internal beta test.

I’ve also been researching tech and prototyping my next project and I’m really excited about it. That’s not to say that I’m leaving my previous projects to rot. Number Crunchers seems to be building up an audience, so it’s only right that I update it with some new stuff. And I still have plenty of future plans to diversify the gameplay in Horde Rush.

Anyway, I was hoping my first post of 2016 would be more informative or educational for the game development community, but it’s been far too long since I’ve updated the blog. All I can say for now is that I have lots of plans for Under the Weather this year, and I’m going to execute these plans to the best of my abilities.

Until next time,

Make it fun!

 

Posted in Thoughts | Leave a comment

Horde Rush 1.02 Update

I’ve updated Horde Rush with a couple of new features and bug fixes!

The biggest difference in this release is the addition of this bad boy:
squash_head_color

This is Squash. Yeah, he’s 48 weeks early for Halloween, but he can throw a pumpkin at you from a distance, so take him down as soon as you can with your arrows. If you can’t, then he’ll try to attack you up close.

I’ve also improved the Shield mechanics. I admit it was pretty difficult to use before. But now, when you shield the Hero from attacks, monsters get stunned for a short time. Not only that, the special meter charges whenever you ward off monster attacks with your shield.

I also fixed a few other annoying bugs, including the bow special attack, which now attacks your closest enemies first!

See the full list of fixes and additions in the release notes here:
http://horderush.com/index.php/release-notes/

Posted in Announcements | Leave a comment