An artisanally made PSD Importer for Godot, written in Rust

Overview

PSD Importer for Godot

Speed up your import workflow ✨
An artisanally made PSD Importer for Godot 3.5, written in Rust.

✨ Getting Started | πŸ“š Documentation | πŸ§‘β€πŸ« Tutorial | 🐘 Follow me | πŸ•ΉοΈ Buy our games


PSD Importer is a tool for speeding up your import workflow using Photoshop's PSD files. If you use a different program but export to PSD it should also work. You can use this tool to mass-export all of your layers as seperate assets or as single files depending on import settings.

If you have a developer on your team that can write code, you can write custom import code per-file so you can make your import specific to your needs!

Getting started

This repo contains the code for the project, but you'll need to be at the release repository to download addon for Godot. I'll try and have an addon available for easy download inside of Godoteasy download inside of Godot.

Download from AssetLib

Unfortunately this option isn't available yet. Stay tuned!

Clone this repository / download the zip

  1. Extract the repository in a folder of your choice.
  2. Run cargo build in the folder. (More details on compilation here)
    • Really sorry it works like this for now, will automate a 100% ready release soon!
  3. Import the project in Godot.
  4. Run the scene to get a taste of the PSD Importer!
  5. Move the addons folder to your Godot project.
  6. Enable the plugin in your Project Settings.
  7. Setup the importer using the documentation or tutorial!

Roadmap

There are a few things I want to tackle before calling a v1:

  • Releasing the addon on the AssetLib
    • Make a release repository
    • Make an automated Github Actions that cross-compiles
  • Write compilation instructions for the Rust aspect of the repository
  • A mascot for the page and project
  • Resolve problems with get_node function and path formatting.
    • Currently there are small nuances that can cause certain paths to not behave in a manner the user wants.
  • Add more sensible defaults for the default import script.
  • Try to eliminate all of the unwrap calls or replace them with expect.
  • Try to eliminate all unsafe code or properly document it.

Feature requests

If for whatever reason the importer doesn't 100% fit your needs, feel free to reach out to me, but make sure you can't easily script this functionality using the custom script option.

Getting Help

There are several places you can get help with PSD Importer for Godot & stay up to date with developments:

License

PSD Importer is available under the MIT License. This means that you can use it in any commercial or noncommercial project. The only requirement is that you need to include attribution in your game's docs. A credit would be very, very nice, too, but isn't required. If you'd like to know more about what this license lets you do, tldrlegal.com have a very nice write up about the MIT license that you might find useful.

Made by Bram Dingelstad & Placeholder Gameworks

PSD Importer was originally made for a game called CraftCraft, but we found it nice to share this technology with the broader world ✨

Support Placeholder Gameworks by buying our games.

Originally written by Bram Dingelstad.

Tutorial

Using the standard addon is quite simple. Simply enable the plugin after installing, and you should see *.psd files pop up in Godot's file explorer. Click on any of them and uncheck the "Dont import" option in the import tab. By default, the importer puts the files in the same directory as the import itself, so perhaps move it to a place where you want your final files to be.

Writing a custom importer

Made a GDScript .gd file and start by extending PsdImportScript. PsdImportScript has one method which is import. It has a few arguments:

  1. plugin which is a reference to the EditorPlugin instance, used to skipping frames
  2. importer which is a pre-setup PsdImporter instance with your PSD file pre-loaded.
  3. options which is a Dictionary holding different import options set by the user.
  4. base_directory which is a path String where the PSD file is situated.

You can navigate the PSD by calling methods on the importer and its resulting PsdNode children, an example below is exporting all of the layers on the root of PSD that start with the letter A.

extends PsdImportScript

func import(plugin, importer, options, base_directory):
	# Go through all the children in the root of the PSD
	for child in importer.get_children():
		match child.node_type:
			'Layer':
				var name = child.name

				# When it the name of the layer doesn't start with A or a, skip this (continue)
				if not name.to_lower().starts_with('a'):
					continue

				var image_path = '%s%s.png' % [base_directory, name]

				# Wait a frame every 
				yield(plugin.get_tree(), 'idle_frame')

				# Get the image
				child.get_image()
				var image = yield(child, 'image') # And await its result
				if image:
					image.save_png(image_path)
					print('Imported "%s" to "%s"' % [name, image_path])
				else:
					printerr('Tried saving image to "%s" but something went wrong' % image_path)

	return OK

Another example, which is default import script, can be found here. It features a recursive exporter, which will go into groups and export sublayers.

Documentation

PsdImporter

Inherits from Object

Object for all interaction with PSD Importer.

Description

This the main importer class that is the entry point for the importer code. You'll use this class to get access to the different layers and groups inside of the PSD. Usually, most users should not have to interact with this class however.

Make sure that you load valid PSD data before resuming with other methods.

Methods

Return value Method name
void load( PoolByteArray psd_bytes )
void print_tree ( )
PsdNode get_node ( String path )
Array get_children ( )
Array get_layers ( )
Array get_groups ( )

Method Descriptions

  • void load ( PoolByteArray psd_bytes )

    Loads in the PSD data into the class to do operations on. You can get psd_bytes by reading from a file using File.

  • void print_tree ( )

    Will print an entire tree of the PSD with layers and groups into Godot's stdout (including your debug console). It can help you track down issues in your code or confirm if your file has been loaded correctly during debugging.

    An example of how that looks:

    [G] Group name
          [G] Another sub-group
              [L] A sub-sub layer (you get the idea)
          [L] Layer that is a child of "Group name" above
          [L] Another Layer
    [L] Layer on the root
    
  • PsdNode get_node ( String path )

    Get a single PsdNode (either a Layer or a Group) and return it. Works similarly to Node.get_node, should be familiar for most Godot programmers.

  • Array get_children ( )

    Gets all direct children as PsdNodes (either Layer or a Group) of the Root of the PSD file.

  • Array get_layers ( )

    Gets all direct children as PsdNodes of the Root of the PSD file, guaranteed to be Layers.

  • Array get_groups ( )

    Gets all direct children as PsdNodes of the Root of the PSD file, guaranteed to be Groups.

PsdNode

Inherits from Reference

A generic node representing either a Layer or a Group in a PSD file.

Description

The result of any get_node call, either on PsdImporter or PsdNode. You can

Properties

Type Property Default value
String name Name of layer / group
String path The path of the layer / group relative from the root
String node_type The type of node either "Layer" or "Group" can be used to differentiate (e.g in match statements)
Dictionary properties Properties of the Layer if node_type is of type "Layer".

Methods

Return value Method name
PsdNode get_node ( String path )
Array get_children ( )
Array get_layers ( )
Array get_groups ( )
void get_image ( )

Signals

  • image ( Image image )

    Emitted when the node is done rendering an image after executing get_image

Property Descriptions

  • String name

    The name of the layer or group, as saved in the PSD file.

  • String path

    The path to the layer or group, relative from the root of the PSD file.

  • String node_type

    The type of the PsdNode, can either be "Layer" or "Group". You can use this to guarantee the type in your code.

    An example, to make sure that you can execute get_image:

      var psd_node = importer.get_node('/Path/to/arbitrary/node')
    
      var image
      match psd_node.node_type:
          "Layer":
              psd_node.get_image()
              image = yield(psd_node, 'image')
    
          _:
              printerr("not a layer containing an image")
              
  • Dictionary properties

    A dictionary containing information of the node in case node_type has the value of "Layer". If the node_type has the value of "Group", this property will be null.

    It has the following values:

    {
      visible: bool,
      pub opacity: int,
      pub width: int,
      pub height: int
    }

Method Descriptions

  • PsdNode get_node ( String path )

    Get a single PsdNode (either a Layer or a Group) and return it. Works similarly to Node.get_node, should be familiar for most Godot programmers.

  • Array get_children ( )

    Gets all direct children as PsdNodes (either Layer or a Group) of the Root of the PSD file.

  • Array get_layers ( )

    Gets all direct children as PsdNodes of the Root of the PSD file, guaranteed to be Layers.

  • Array get_groups ( )

    Gets all direct children as PsdNodes of the Root of the PSD file, guaranteed to be Groups.

  • void get_image ( )

    Starts converting the Layer into an Image, will only work if node_type is of type "Layer". Result of this function is captured using the image signal.

    An example of how to get an image:

    var bytes = ... # PoolByteArray with PSD data
    
    # Create an importer
    var importer = PsdImporter.new()
    importer.load(bytes)
    
    # Get the psd node, which we assume to be of `node_type` "Layer"
    var psd_node = importer.get_node('/Path/to/a/Layer')
    
    # Start image rendering process
    psd_node.get_image()
    # Wait until it's done
    var image = yield(psd_node, 'image')
    
You might also like...
Simple retro game made using Rust bracket-lib by following "Herbert Wolverson's Hands on Rust" book.

Flappy Dragon Code from This program is a result of a tutorial i followed from Herbert Wolverson's Hands-on Rust Effective Learning through 2D Game De

Endless Trial is a simple 2D bullet-hell-like game made in Rust with Tetra.

Endless Trial Endless Trial is a simple 2D bullet-hell-like game made in Rust with Tetra. To-do Sound Credits This project uses several free sprites:

A highly customizable snake clone made in Rust with the Bevy engine, named after the Japanese word for snake, 蛇.
A highly customizable snake clone made in Rust with the Bevy engine, named after the Japanese word for snake, 蛇.

Hebi 🐍 A highly customizable snake clone made in Rust with the Bevy engine, named after the Japanese word for snake, 蛇(へび). Configuration One of the

2-player game made with Rust and
2-player game made with Rust and "ggez" engine, based on "Conway's Game of Life"

fight-for-your-life A 2-player game based on the "Conway's Game of Life", made with Rust and the game engine "ggez". Create shapes on the grid that wi

Fish Fight is a tactical 2D shooter. Made with Rust-lang and Macroquad πŸ¦€πŸŒΆ
Fish Fight is a tactical 2D shooter. Made with Rust-lang and Macroquad πŸ¦€πŸŒΆ

Fish Fight Introduction Fish Fight is a tactical 2D shooter, played by up to 4 players online or on a shared screen. Aim either left or right; the res

UnoLife is a project made solely in rust for the Timathon jam.
UnoLife is a project made solely in rust for the Timathon jam.

UnoLife, the way of unordinary life This is the perfect app for anyone looking to spice up their life! It has a bunch of apps and features to help you

The official Game Engine of the Li Institution - Mostly made in Rust. Still WIP

Nengine The Nengine is an open sourced game engine made by the Li Institution. As of writing this, it is in very early development and is definitely n

A Serious (yet fun) Game made in Rust with Bevy

The Omega Ai Test for Brazilian Biome Image Classification PLAY A Serious (yet fun) Game made in Rust with Bevy How well do you know the Brazilian bio

A game made for the Rusty Jam https://itch.io/jam/rusty-jam
A game made for the Rusty Jam https://itch.io/jam/rusty-jam

Murder-User Dungeon Introduction Tony is a young man. Finally having its own apartment is a good thing! He will learn how to live by himself and how t

Owner
Bram Dingelstad
Indiegame developer & activist.
Bram Dingelstad
A tool to generate inbetweens for animated sprites, written in godot-rust

Bitmapflow is a tool to help you generate inbetweens for animated sprites. In other words, it makes your animations smoother. It uses optical flow to

null 411 Dec 21, 2022
A CLI tool to manage your godot-rust projects

ftw A CLI tool to manage your godot-rust project! Table of contents General Information Setup Usage Contact General Information This is a tool to help

Michael Angelo Calimlim 77 Dec 13, 2022
compare gdnative rust based physics against Godot built-in physics

Godot vs. Rapier Rapier is an open source physics framework written in Rust. This project pits godots built-in physics against Rapier. It uses godot-r

Stephan Dilly 75 Nov 17, 2022
An egui backend for godot-rust

Godot Egui An egui backend for godot-rust. Rationale Godot has a perfectly valid GUI system, so why egui? Here are my personal reasons: Simplicity: No

null 109 Jan 4, 2023
jlang--godot bridge, built in rust

jlang-rs-gd J is an extremely high-level mathematical notation and programming language. Godot is a game / gui / multimedia engine. jlang-rs-gd lets y

tangentstorm 2 Feb 15, 2022
GDDB is a superfast in-memory database designed for use in Godot

GDDB GDDB is a superfast in-memory database designed for use in Godot. This database aims to provide an easy frontend to an efficient in-memory databa

Richard Patching 5 Dec 4, 2022
A Godot 3.4 binding for Live2D

godot-cubism A Godot 3.4 binding for cubism-rs which itself is a binding for the native cubism sdk. Usage var factory = load("path_to_your_native_scri

null 16 Dec 23, 2022
A crate for using Bevy with the Godot Engine.

bevy_godot A crate for using Bevy with the Godot Engine. This crate is in active development and is not ready for production use. Features Godot Scene

Abby Bryant 63 Dec 17, 2022
Plugin to generate flowfields from tilemaps in the Godot Engine!

Godot Tilemap Flowfields RTS-Style optimized path-finding for crowds of agents. Built for the Godot game engine, written with performance in mind in R

Arne Winter 18 Jan 10, 2023
A direct ecs to low-level server implementation for Godot 4.1

godot_ecs What if Godot 4.1 and Bevy got married? Well, you'd get one interesting duo of data driven goodness. In Development This crate is not produc

null 5 Oct 6, 2023