Skip to content

Instantly share code, notes, and snippets.

@robstenzinger
Created January 20, 2021 22:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robstenzinger/eef66ca54ddc5fae7dfac190e9f88dd2 to your computer and use it in GitHub Desktop.
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.
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