Created
January 20, 2021 22:16
-
-
Save robstenzinger/eef66ca54ddc5fae7dfac190e9f88dd2 to your computer and use it in GitHub Desktop.
Takes visual game screen layouts in PSD files, exports the layers in different resolutions, and saves layout data as JSON and Lua. Currently using this for a game built with the Solar2D game platform.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
import sys | |
from psd_tools import PSDImage | |
import json | |
import shutil | |
import math | |
from PIL import Image | |
# target resolutions | |
primary_resolution = (480,320) | |
target_resolutions = (1, 2.5, 3, 4) | |
# asset layout files | |
images_to_process = [ | |
"START.psd", | |
] | |
# for testing and improving this script | |
export_images = True | |
""" | |
Steps to get images ready to use in the game: | |
1. configure files: list of images to process | |
2. configure base resolution, width/height, portrait/landscape remember to manage/plan for along the way | |
3. configure target resolutions the names and multipliers for scale | |
4. for each input image | |
- it's in the authoring resolution which will likely be one of the multiplier resolutions | |
- todo: consider making exported layers as one or more texture atlases | |
- save the .json and .lua meta data files (for given X resolution) | |
- repeat for each of the outputs, 2x, 2.5x, 3x, 4x, etc. | |
5. PNG crush all the PNGs - NOT YET AUTOMATED | |
6. finish file management, copy to proper destinations - NOT YET AUTOMATED | |
7. run test to verify (simple test app) - NOT YET AUTOMATED | |
""" | |
def process_images(): | |
global target_resolutions | |
global images_to_process | |
global primary_resolution | |
# repeat for each target resolution | |
for current_image_file_name in images_to_process: | |
if current_image_file_name.lower().endswith("psd"): | |
file_name, file_extension = os.path.splitext(current_image_file_name) | |
# save working copy in a separate psds folder | |
path_psd_working_folder = file_name + "_psds" | |
if not os.path.exists(path_psd_working_folder): | |
os.makedirs(path_psd_working_folder) | |
# same working file for all the resolutions | |
file_psd_working = file_name + ".psd" | |
file_psd_working_path = os.path.join(path_psd_working_folder, file_psd_working) | |
# work with a copy of the source file | |
if os.path.exists(file_psd_working_path): | |
os.remove(file_psd_working_path) | |
shutil.copyfile(current_image_file_name, file_psd_working_path) | |
# save results in a subfolder for this given layout image | |
if os.path.exists(file_name): | |
shutil.rmtree(file_name) | |
if not os.path.exists(file_name): | |
os.makedirs(file_name) | |
# getting started by opening the photoshop file working copy | |
print("opening: " + file_psd_working_path) | |
img = PSDImage.open(file_psd_working_path) | |
for current_resolution_multiplier in target_resolutions: | |
target_width = primary_resolution[0] * current_resolution_multiplier | |
multiplier_suffix = "" | |
if current_resolution_multiplier > 1: | |
multiplier_suffix = "@" + str(current_resolution_multiplier) + "x" | |
# file and folder names | |
file_psd_working = file_name + multiplier_suffix + ".psd" | |
file_json = file_name + multiplier_suffix + ".json" | |
file_lua = file_name + multiplier_suffix + ".lua" | |
# determine the size ratio difference between the current resolution and the size of the source PSD | |
size_ratio = target_width / img.width | |
layer_data = image_export(img, file_name, multiplier_suffix, size_ratio) | |
# individual image json | |
export_json(file_name, file_json, layer_data) | |
# individual image lua | |
export_lua(file_name, file_lua, layer_data) | |
# todo: crush optimize the PNGs | |
# todo: final file management | |
# settings for a given asset layout file | |
def image_export(img, file_name, multiplier_suffix, size_ratio): | |
global export_images # useful for testing | |
# each layer in the image needs to be exported and have it's layout data stored | |
print("gathering layers data") | |
layer_data = [] | |
for layer in img: | |
print(layer.name) | |
if("::" not in layer.name): | |
# gather layer layout information | |
l = {} | |
l["name"] = layer.name | |
l["width"] = math.ceil(layer.width * size_ratio) | |
l["height"] = math.ceil(layer.height * size_ratio) | |
l["x"] = math.ceil(layer.left * size_ratio) | |
l["y"] = math.ceil(layer.top * size_ratio) | |
layer_data.append(l) | |
# export layer as image asset | |
if export_images is True: | |
if int(l["width"]) > 0 and int(l["height"]) > 0: | |
layer_output_raw_path = os.path.join(file_name, layer.name + "_raw" + ".png") | |
layer_output_sizefixed_path = os.path.join(file_name, layer.name + multiplier_suffix + ".png") | |
if os.path.exists(layer_output_raw_path) == False: | |
layer_img = layer.composite() | |
layer_img.save(layer_output_raw_path) | |
# resize the layer to be appropriate for this resolution/size ratio | |
final_img_raw = Image.open(layer_output_raw_path) | |
final_img_resized = final_img_raw.resize((l["width"], l["height"])) | |
final_img_resized.save(layer_output_sizefixed_path) | |
final_img_resized = None | |
final_img_raw = None | |
return layer_data | |
def export_json(file_name, file_json, layer_data): | |
# json output | |
f = open(os.path.join(file_name,file_json), "w") | |
json.dump(layer_data, f) | |
def export_lua(file_name, file_lua, layer_data): | |
# lua output | |
layer_data_strings = [] | |
layer_data_strings.append("module(..., package.seeall)") | |
layer_data_strings.append("") | |
layer_data_strings.append("game_layout = {}") | |
layer_data_strings.append("") | |
for elem in layer_data: | |
current_layer = "" | |
current_layer += """game_layout["{0}"] = """.format(elem["name"]) | |
current_layer += """{{ name="{0}" """.format(elem["name"]) | |
current_layer += """ ,width={0} """.format(elem["width"]) | |
current_layer += """ ,height={0} """.format(elem["height"]) | |
current_layer += """ ,x={0} """.format(elem["x"]) | |
current_layer += """ ,y={0} }}""".format(elem["y"]) | |
layer_data_strings.append(current_layer) | |
f = open(os.path.join(file_name,file_lua), "w") | |
f.write("\n".join(layer_data_strings)) | |
f.close() | |
if __name__ == "__main__": | |
process_images() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment