├── .gitignore ├── .travis.yml ├── test ├── expected_unknown.txt ├── helper.sh ├── pack_unity_packager_exec_test.sh ├── expected_help.txt ├── runner └── pack_unity_package_test.sh ├── TexturePacker ├── Editor.meta ├── Plugins.meta ├── Shaders.meta ├── Shaders │ ├── OpaqueUnlit.shader.meta │ ├── VertexColor.shader.meta │ ├── TransparentUnlit.shader.meta │ ├── VertexColor.shader │ ├── OpaqueUnlit.shader │ └── TransparentUnlit.shader ├── Plugins │ ├── MiniJSON.cs.meta │ ├── TexturePacker.cs.meta │ ├── TexturePacker.cs │ └── MiniJSON.cs └── Editor │ ├── MeshTweaker.cs.meta │ ├── TexturePackerImport.cs.meta │ ├── MeshTweaker.cs │ └── TexturePackerImport.cs ├── Gemfile ├── Guardfile ├── LICENSE ├── README.md └── pack_unity_package /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | /Gemfile.lock 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | install: 3 | - sudo apt-get install shunit2 4 | 5 | script: ./test/runner -------------------------------------------------------------------------------- /test/expected_unknown.txt: -------------------------------------------------------------------------------- 1 | Unknown flag - detected. 2 | Usage: ./pack_unity_package [-h] [-v] [-u unityPath] [-f folderPath] [-o outputFile] 3 | -------------------------------------------------------------------------------- /TexturePacker/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 35797fecbc4077c48bede249369aa979 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /TexturePacker/Plugins.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9e2bac7ba4091ca4cbabad46a64db30e 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /TexturePacker/Shaders.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e7d35e47b5c230847b63dcaab2604648 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | group :development do 4 | gem 'guard-shell' 5 | gem 'rb-fsevent', :require => false if RUBY_PLATFORM =~ /darwin/i 6 | end -------------------------------------------------------------------------------- /TexturePacker/Shaders/OpaqueUnlit.shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 59bb2c52ea3e43543bc4008d78550355 3 | ShaderImporter: 4 | defaultTextures: [] 5 | userData: 6 | -------------------------------------------------------------------------------- /TexturePacker/Shaders/VertexColor.shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 82019b3dc63e18044875f19e7ae7ad51 3 | ShaderImporter: 4 | defaultTextures: [] 5 | userData: 6 | -------------------------------------------------------------------------------- /TexturePacker/Shaders/TransparentUnlit.shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d8f45406dc6b7444fba8ee3589b2fc82 3 | ShaderImporter: 4 | defaultTextures: [] 5 | userData: 6 | -------------------------------------------------------------------------------- /test/helper.sh: -------------------------------------------------------------------------------- 1 | SHUNIT2=/usr/bin/shunit2 2 | 3 | export PREFIX="$PWD/test" 4 | export PATH="$PWD/bin:$PATH" 5 | 6 | test_path="$PATH" 7 | 8 | setUp() { return; } 9 | tearDown() { return; } 10 | oneTimeTearDown() { return; } -------------------------------------------------------------------------------- /TexturePacker/Plugins/MiniJSON.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 550c5564dbfbc9242b32721e623ec9f3 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /TexturePacker/Editor/MeshTweaker.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2df93da18054372419b7c4cc84774cd3 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /TexturePacker/Plugins/TexturePacker.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dc14a033102b02e4b8c66bb8b1e25f54 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /TexturePacker/Editor/TexturePackerImport.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6e8b6e08c141d294aacd0e4153efb95f 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /test/pack_unity_packager_exec_test.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | . ./test/helper.sh 3 | 4 | testExecNoArguments() 5 | { 6 | ./pack_unity_package -d 2>/dev/null 7 | assertEquals "did not exit with 0" 0 $? 8 | } 9 | 10 | # run shunit2 11 | SHUNIT_PARENT=$0 . $SHUNIT2 -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | # A sample Guardfile 2 | # More info at https://github.com/guard/guard#readme 3 | 4 | # Add files and commands to this file, like the example: 5 | # watch(%r{file/path}) { `command(s)` } 6 | # 7 | guard :shell do 8 | watch(/(pack_unity_package)|(.*).sh/) { `sh test/runner` } 9 | end 10 | -------------------------------------------------------------------------------- /test/expected_help.txt: -------------------------------------------------------------------------------- 1 | usage: pack_unity_package [-h] [-v] [-u unityPath] [-f folderPath] [-o outputFile] 2 | 3 | Options 4 | -v show version 5 | -f set folder to bundle 6 | -o set filename to output 7 | -u set unity command path 8 | (-h) show this help 9 | -------------------------------------------------------------------------------- /test/runner: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function log() { 4 | if [[ -t 1 ]]; then 5 | echo -e "\x1b[1m\x1b[32m>>>\x1b[0m \x1b[1m\x1b[37m$1\x1b[0m" 6 | else 7 | echo ">>> $1" 8 | fi 9 | } 10 | 11 | error=0 12 | for test in ${0%/*}/*_test.sh; do 13 | log "Running $test ..." 14 | $test || error=1 15 | echo 16 | done 17 | -------------------------------------------------------------------------------- /TexturePacker/Shaders/VertexColor.shader: -------------------------------------------------------------------------------- 1 | Shader "Sprites/Transparent Vertex Color" { 2 | Properties { 3 | _MainTex ("Texture", 2D) = "white" {} 4 | } 5 | 6 | Category { 7 | Tags { "Queue"="Transparent" } 8 | Lighting Off 9 | ZWrite Off 10 | 11 | Blend SrcAlpha OneMinusSrcAlpha 12 | 13 | BindChannels { 14 | Bind "Color", color 15 | Bind "Vertex", vertex 16 | Bind "TexCoord", texcoord 17 | } 18 | 19 | SubShader { 20 | Pass { 21 | SetTexture [_MainTex] { 22 | Combine texture * primary DOUBLE 23 | } 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /TexturePacker/Shaders/OpaqueUnlit.shader: -------------------------------------------------------------------------------- 1 | Shader "Sprites/Opaque Unlit" 2 | { 3 | Properties 4 | { 5 | _Color ("Color Tint", Color) = (1,1,1,1) 6 | _MainTex ("Base (RGB)", 2D) = "white" 7 | } 8 | 9 | Category 10 | { 11 | Lighting Off 12 | ZWrite On 13 | Cull back 14 | Tags {Queue=Geometry} 15 | 16 | SubShader 17 | { 18 | 19 | Pass 20 | { 21 | SetTexture [_MainTex] 22 | { 23 | ConstantColor [_Color] 24 | Combine Texture * constant 25 | } 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /TexturePacker/Shaders/TransparentUnlit.shader: -------------------------------------------------------------------------------- 1 | Shader "Sprites/Transparent Unlit" 2 | { 3 | Properties 4 | { 5 | _Color ("Color Tint", Color) = (1,1,1,1) 6 | _MainTex ("Base (RGB) Alpha (A)", 2D) = "white" 7 | } 8 | 9 | Category 10 | { 11 | Lighting Off 12 | //ZWrite Off 13 | ZWrite On // uncomment if you have problems like the sprite disappear in some rotations. 14 | Cull back 15 | Blend SrcAlpha OneMinusSrcAlpha 16 | //AlphaTest Greater 0.001 // uncomment if you have problems like the sprites or 3d text have white quads instead of alpha pixels. 17 | Tags {Queue=Transparent} 18 | 19 | SubShader 20 | { 21 | 22 | Pass 23 | { 24 | SetTexture [_MainTex] 25 | { 26 | ConstantColor [_Color] 27 | Combine Texture * constant 28 | } 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (C) 2013 Mitch Thompson, Harald Lurger, Hays Clark 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Unity3D - Texture Packer Importer 2 | [![Build Status](https://travis-ci.org/haysclark/unity3d-texturepackerimporter.png)](https://travis-ci.org/haysclark/unity3d-texturepackerimporter) 3 | 4 | Grab the latest Unity Package here!
5 | [TexturePackerImporter.unitypackage](http://haysclark.github.io/unity3d-texturepackerimporter/package/TexturePackerImporter.unitypackage) 6 | 7 | ## Authors 8 | * Mitch Thompson 9 | * Harald Lurger 10 | * Hays Clark 11 | 12 | ## Video Tutorial 13 | There is a tutorial for this plugin here: 14 | http://www.youtube.com/watch?v=CHQmvC1pqaY 15 | 16 | ## Instructions 17 | How to import texture sheets from Texture Packer 18 | 19 | ### TexturePacker settings: 20 | Data Format: Unity3D (or JSON Hashtable, then change extension from .json to .txt so Unity picks it up as a text asset) 21 | Allow rotation is OK 22 | Everything else at your discretion 23 | Power of 2 output textures are suggested. 24 | 25 | ### Unity process: 26 | Create a folder in your Assets/ directory for your imported sprites. 27 | Copy the TXT and Image file (PNG, TGA, etc) into that folder. 28 | Your paths should look something like: 29 | Assets/MySprite/MySprite.txt 30 | Assets/MySprite/MySprite.png 31 | 32 | ### Shaders: 33 | Transparent Unlit - 34 | The default shader for all imported sprite sheets. 35 | Opaque Unlit - 36 | nontransparent tintable shader great for drawing backgrounds that don't need alpha. Very efficient. 37 | Vertex Color - 38 | Does not have an inspector-tweakable color property. All colors must be set by altering the colors[] or colors32[] array of a given mesh. 39 | Supports both texture alpha and vertex color alpha. 40 | 41 | ## About Texture Packer 42 | http://www.texturepacker.com/ 43 | 44 | -------------------------------------------------------------------------------- /TexturePacker/Editor/MeshTweaker.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Mesh Tools - Mesh Tweaker 3 | Copyright (C) 2013 Mitch Thompson 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program. If not, see . 17 | */ 18 | 19 | using UnityEngine; 20 | using UnityEditor; 21 | using System.Collections; 22 | using System.Collections.Generic; 23 | 24 | public class MeshTweaker{ 25 | 26 | [MenuItem("Assets/MeshTweaker/Rotate/X45")] 27 | static void RotateX45(){BatchRotate(45,0,0);} 28 | [MenuItem("Assets/MeshTweaker/Rotate/X90")] 29 | static void RotateX90(){BatchRotate(90,0,0);} 30 | 31 | [MenuItem("Assets/MeshTweaker/Rotate/Y45")] 32 | static void RotateY45(){BatchRotate(0,45,0);} 33 | [MenuItem("Assets/MeshTweaker/Rotate/Y90")] 34 | static void RotateY90(){BatchRotate(0,90,0);} 35 | 36 | [MenuItem("Assets/MeshTweaker/Rotate/Z45")] 37 | static void RotateZ45(){BatchRotate(0,0,45);} 38 | [MenuItem("Assets/MeshTweaker/Rotate/Z90")] 39 | static void RotateZ90(){BatchRotate(0,0,90);} 40 | 41 | static void BatchRotate(float x, float y, float z){ 42 | Quaternion quat = Quaternion.Euler(x,y,z); 43 | foreach(Object o in Selection.objects){ 44 | if(o is Mesh){ 45 | RotateMesh(o as Mesh, quat); 46 | } 47 | } 48 | } 49 | 50 | static void RotateMesh(Mesh mesh, Quaternion quat){ 51 | Vector3[] verts = mesh.vertices; 52 | for(int i = 0; i < verts.Length; i++){ 53 | verts[i] = quat * verts[i]; 54 | } 55 | 56 | mesh.vertices = verts; 57 | 58 | mesh.RecalculateNormals(); 59 | 60 | EditorUtility.SetDirty(mesh); 61 | } 62 | 63 | 64 | [MenuItem("Assets/MeshTweaker/Align/Center")] 65 | static void BatchCenter(){ 66 | foreach(Object o in Selection.objects){ 67 | if(o is Mesh){ 68 | CenterMesh(o as Mesh); 69 | } 70 | } 71 | } 72 | 73 | static void CenterMesh(Mesh mesh){ 74 | Vector3[] verts = mesh.vertices; 75 | 76 | Vector3 min = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue); 77 | Vector3 max = new Vector3(float.MinValue, float.MinValue, float.MinValue); 78 | 79 | foreach(Vector3 v in verts){ 80 | if(v.x < min.x) min.x = v.x; 81 | else if(v.x > max.x) max.x = v.x; 82 | 83 | if(v.y < min.y) min.y = v.y; 84 | else if(v.y > max.y) max.y = v.y; 85 | 86 | if(v.z < min.z) min.z = v.z; 87 | else if(v.z > max.z) max.z = v.z; 88 | } 89 | 90 | Vector3 average = (min + max) * 0.5f; 91 | 92 | for(int i = 0; i < verts.Length; i++){ 93 | verts[i] = verts[i] + ( Vector3.zero - average ); 94 | } 95 | 96 | mesh.vertices = verts; 97 | EditorUtility.SetDirty(mesh); 98 | } 99 | } -------------------------------------------------------------------------------- /test/pack_unity_package_test.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | . ./test/helper.sh 3 | 4 | DEFAULT_EXPORT_FLAG="-exportPackage" 5 | DEFAULT_EXPORT_PATH="TexturePacker" 6 | DEFAULT_EXPORT_DST="Assets/TexturePacker" 7 | DEFAULT_OUTPUT="TexturePackerImporter.unitypackage" 8 | 9 | EXPECTED_CREATE_PROJECT="/Applications/Unity/Unity.app/Contents/MacOS/Unity -quit -batchmode -createProject .;" 10 | EXPECTED_COPY_CONTENTS="cp -r TexturePacker Assets/TexturePacker;" 11 | 12 | oneTimeTearDown() 13 | { 14 | rm ./results.txt 15 | } 16 | 17 | testScriptShouldOutputExpectedUnknown() 18 | { 19 | EXPECTED='Expected output differs.' 20 | ./pack_unity_package -b > ./results.txt 21 | diff ./test/expected_unknown.txt ./results.txt 22 | assertTrue "${EXPECTED}" $? 23 | } 24 | 25 | testHFlagShouldOutputHelp() 26 | { 27 | EXPECTED='Expected output differs.' 28 | ./pack_unity_package -h > ./results.txt 29 | diff ./test/expected_help.txt ./results.txt 30 | assertTrue "${EXPECTED}" $? 31 | } 32 | 33 | testScriptShouldOutputExpectedVersion() 34 | { 35 | EXPECTED="pack_unity_package version 1.0" 36 | ./pack_unity_package -d -v > ./results.txt 37 | firstline=`head -1 ./results.txt` 38 | assertEquals "${EXPECTED}" "${firstline}" 39 | } 40 | 41 | testScriptShouldWarnIfUnityIsMissing() 42 | { 43 | EXPECTED="Unity path is incorrect or missing." 44 | ./pack_unity_package -d -u "/fake" > ./results.txt 45 | firstline=`head -1 ./results.txt` 46 | assertEquals "${EXPECTED}" "${firstline}" 47 | } 48 | 49 | testScriptShouldOutputCmdOnDryRunFlag() 50 | { 51 | EXPECTED="$EXPECTED_CREATE_PROJECT$EXPECTED_COPY_CONTENTS/Applications/Unity/Unity.app/Contents/MacOS/Unity -quit -batchmode ${DEFAULT_EXPORT_FLAG} ${DEFAULT_EXPORT_DST} ${DEFAULT_OUTPUT};" 52 | ./pack_unity_package -d > ./results.txt 53 | firstline=`head -1 ./results.txt` 54 | assertEquals "${EXPECTED}" "${firstline}" 55 | } 56 | 57 | testScriptShouldWarnIfFolderIsMissing() 58 | { 59 | EXPECTED="Folder path is incorrect or missing." 60 | ./pack_unity_package -d -f "/fake" > ./results.txt 61 | firstline=`head -1 ./results.txt` 62 | assertEquals "${EXPECTED}" "${firstline}" 63 | } 64 | 65 | testScriptShouldUseCorrectFolderWhenFFlagIsSet() 66 | { 67 | mkdir "fake" 68 | EXPECTED_FILE="fake" 69 | EXPECTED_ASSETS_PATH=Assets/${EXPECTED_FILE} 70 | EXPECTED_COPY="cp -r ${EXPECTED_FILE} ${EXPECTED_ASSETS_PATH};" 71 | EXPECTED="$EXPECTED_CREATE_PROJECT$EXPECTED_COPY/Applications/Unity/Unity.app/Contents/MacOS/Unity -quit -batchmode ${DEFAULT_EXPORT_FLAG} ${EXPECTED_ASSETS_PATH} ${DEFAULT_OUTPUT};" 72 | ./pack_unity_package -d -f "${EXPECTED_FILE}" > ./results.txt 73 | firstline=`head -1 ./results.txt` 74 | assertEquals "${EXPECTED}" "${firstline}" 75 | rmdir "fake" 76 | } 77 | 78 | testScriptShouldUseCorrectUnityFlagWhenFFlagIsSet() 79 | { 80 | mkdir "fake" 81 | EXPECTED_FILE="fake" 82 | EXPECTED_ASSETS_PATH=Assets/${EXPECTED_FILE} 83 | EXPECTED_COPY="cp -r ${EXPECTED_FILE} ${EXPECTED_ASSETS_PATH};" 84 | EXPECTED_COMMAND="exportPackage" 85 | EXPECTED="$EXPECTED_CREATE_PROJECT$EXPECTED_COPY/Applications/Unity/Unity.app/Contents/MacOS/Unity -quit -batchmode -${EXPECTED_COMMAND} ${EXPECTED_ASSETS_PATH} ${DEFAULT_OUTPUT};" 86 | ./pack_unity_package -d -f "${EXPECTED_FILE}" > ./results.txt 87 | firstline=`head -1 ./results.txt` 88 | assertEquals "${EXPECTED}" "${firstline}" 89 | rmdir "fake" 90 | } 91 | 92 | testScriptShouldUseCorrectOutputPathWhenOFlagIsSet() 93 | { 94 | EXPECTED_PATH="expectedOutputPath" 95 | EXPECTED="$EXPECTED_CREATE_PROJECT$EXPECTED_COPY_CONTENTS/Applications/Unity/Unity.app/Contents/MacOS/Unity -quit -batchmode ${DEFAULT_EXPORT_FLAG} ${DEFAULT_EXPORT_DST} ${EXPECTED_PATH};" 96 | ./pack_unity_package -d -o "${EXPECTED_PATH}" > ./results.txt 97 | firstline=`head -1 ./results.txt` 98 | assertEquals "${EXPECTED}" "${firstline}" 99 | } 100 | 101 | # run shunit2 102 | SHUNIT_PARENT=$0 . $SHUNIT2 -------------------------------------------------------------------------------- /pack_unity_package: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Export Unity3d-TexturePacker to Unity Bundle 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | # this software and associated documentation files (the "Software"), to deal in 7 | # the Software without restriction, including without limitation the rights to 8 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | # of the Software, and to permit persons to whom the Software is furnished to do 10 | # so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | # 23 | # Latest version can be found at https://github.com/haysclark/unity3d-texturepackerimporter 24 | # Tested on OSX (10.9) 25 | # 26 | # Copyright (c) 2013 Hays Clark 27 | # 28 | 29 | UNITY_PATH="/Applications/Unity/Unity.app/Contents/MacOS/Unity" 30 | FOLDER_PATH="TexturePacker" 31 | OUTPUT_PATH="TexturePackerImporter.unitypackage" 32 | 33 | # vars 34 | NAME=`basename "$0"` 35 | VERSION=1.0 36 | DRY_RUN=0 37 | EXPECTED_FLAGS="[-h] [-v] [-u unityPath] [-f folderPath] [-o outputFile]" 38 | CMD="" 39 | 40 | PRINT_VERSION(){ 41 | echo ${NAME} version ${VERSION} 42 | } 43 | 44 | PRINT_HELP(){ 45 | echo usage: ${NAME} ${EXPECTED_FLAGS} 46 | echo 47 | echo "Options" 48 | echo " -v show version" 49 | echo " -f set folder to bundle" 50 | echo " -o set filename to output" 51 | echo " -u set unity command path" 52 | echo "(-h) show this help" 53 | } 54 | 55 | COPY_README(){ 56 | if [ -d "Assets/" ]; then 57 | cp LICENSE Assets/LICENSE.txt 58 | fi 59 | } 60 | 61 | COPY_LICENSE(){ 62 | if [ -d "Assets/" ]; then 63 | cp README.md Assets/README.txt 64 | fi 65 | } 66 | 67 | CLEAN_UP(){ 68 | if [ -d "Assets/" ]; then 69 | rm -r Assets/ 70 | fi 71 | if [ -d "ProjectSettings/" ]; then 72 | rm -r ProjectSettings/ 73 | fi 74 | if [ -d "Library/" ]; then 75 | rm -r Library/ 76 | fi 77 | if [ -d "Temp/" ]; then 78 | rm -r Temp/ 79 | fi 80 | } 81 | 82 | while getopts "dvf:o:u:h" VALUE "${@}" ; do 83 | if [ "${VALUE}" = "h" ] ; then 84 | PRINT_HELP 85 | exit 0 86 | fi 87 | if [ "${VALUE}" = "v" ] ; then 88 | PRINT_VERSION 89 | fi 90 | if [ "${VALUE}" = "d" ] ; then 91 | DRY_RUN=1 92 | fi 93 | if [ "${VALUE}" = "f" ] ; then 94 | FOLDER_PATH="${OPTARG}" 95 | fi 96 | if [ "${VALUE}" = "o" ] ; then 97 | OUTPUT_PATH="${OPTARG}" 98 | fi 99 | if [ "${VALUE}" = "u" ] ; then 100 | UNITY_PATH="${OPTARG}" 101 | if ! [ -f "${UNITY_PATH}" ]; then 102 | echo "Unity path is incorrect or missing." 103 | exit 1 104 | fi 105 | fi 106 | if [ "${VALUE}" = ":" ] ; then 107 | echo "Flag -${OPTARG} requires an argument." 108 | echo "Usage: $0 ${EXPECTED_FLAGS}" 109 | exit 1 110 | fi 111 | if [ "${VALUE}" = "?" ] ; then 112 | echo "Unknown flag -${OPTARG} detected." 113 | echo "Usage: $0 ${EXPECTED_FLAGS}" 114 | exit 1 115 | fi 116 | done 117 | 118 | if ! [ -d "${FOLDER_PATH}" ]; then 119 | echo "Folder path is incorrect or missing." 120 | exit 1 121 | fi 122 | 123 | if [ -f "${OUTPUT_PATH}" ]; then 124 | rm ${OUTPUT_PATH}; 125 | fi 126 | 127 | EXPORT_PATH="Assets/${FOLDER_PATH}" 128 | 129 | CMD+="${UNITY_PATH} -quit -batchmode -createProject .;" 130 | COPY_README 131 | COPY_LICENSE 132 | CMD+="cp -r ${FOLDER_PATH} $EXPORT_PATH;" 133 | CMD+="${UNITY_PATH} -quit -batchmode -exportPackage ${EXPORT_PATH} ${OUTPUT_PATH};" 134 | 135 | if [ "${DRY_RUN}" = 1 ] ; then 136 | echo "${CMD}" 137 | exit 0 138 | fi 139 | 140 | eval "${CMD}" 141 | CLEAN_UP 142 | exit 0 -------------------------------------------------------------------------------- /TexturePacker/Plugins/TexturePacker.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Mitch Thompson 3 | Extended by Harald Lurger (2013) (Process to Sprites) 4 | 5 | Standard MIT License 6 | 7 | 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: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | 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. 12 | */ 13 | 14 | #if UNITY_EDITOR 15 | 16 | using UnityEngine; 17 | using UnityEditor; 18 | using System.Collections; 19 | using System.Collections.Generic; 20 | 21 | public static class TexturePackerExtensions{ 22 | public static Rect TPHashtableToRect(this Hashtable table){ 23 | return new Rect((float)table["x"], (float)table["y"], (float)table["w"], (float)table["h"]); 24 | } 25 | 26 | public static Vector2 TPHashtableToVector2(this Hashtable table){ 27 | if(table.ContainsKey("x") && table.ContainsKey("y")){ 28 | return new Vector2((float)table["x"], (float)table["y"]); 29 | } 30 | else{ 31 | return new Vector2((float)table["w"], (float)table["h"]); 32 | } 33 | } 34 | 35 | public static Vector2 TPVector3toVector2(this Vector3 vec){ 36 | return new Vector2(vec.x, vec.y); 37 | } 38 | 39 | public static bool IsTexturePackerTable(this Hashtable table){ 40 | if(table == null) return false; 41 | 42 | if(table.ContainsKey("meta")){ 43 | Hashtable metaTable = (Hashtable)table["meta"]; 44 | if(metaTable.ContainsKey("app")){ 45 | return true; 46 | // if((string)metaTable["app"] == "http://www.texturepacker.com"){ 47 | // return true; 48 | // } 49 | } 50 | } 51 | 52 | return false; 53 | } 54 | } 55 | 56 | public class TexturePacker{ 57 | 58 | public class PackedFrame{ 59 | public string name; 60 | public Rect frame; 61 | public Rect spriteSourceSize; 62 | public Vector2 sourceSize; 63 | public bool rotated; 64 | public bool trimmed; 65 | Vector2 atlasSize; 66 | 67 | public PackedFrame(string name, Vector2 atlasSize, Hashtable table){ 68 | this.name = name; 69 | this.atlasSize = atlasSize; 70 | 71 | frame = ((Hashtable)table["frame"]).TPHashtableToRect(); 72 | spriteSourceSize = ((Hashtable)table["spriteSourceSize"]).TPHashtableToRect(); 73 | sourceSize = ((Hashtable)table["sourceSize"]).TPHashtableToVector2(); 74 | rotated = (bool)table["rotated"]; 75 | trimmed = (bool)table["trimmed"]; 76 | } 77 | 78 | public Mesh BuildBasicMesh(float scale, Color32 defaultColor){ 79 | return BuildBasicMesh(scale, defaultColor, Quaternion.identity); 80 | } 81 | 82 | public Mesh BuildBasicMesh(float scale, Color32 defaultColor, Quaternion rotation){ 83 | Mesh m = new Mesh(); 84 | Vector3[] verts = new Vector3[4]; 85 | Vector2[] uvs = new Vector2[4]; 86 | Color32[] colors = new Color32[4]; 87 | 88 | if(!rotated){ 89 | verts[0] = new Vector3(frame.x,frame.y,0); 90 | verts[1] = new Vector3(frame.x,frame.y+frame.height,0); 91 | verts[2] = new Vector3(frame.x+frame.width,frame.y+frame.height,0); 92 | verts[3] = new Vector3(frame.x+frame.width,frame.y,0); 93 | } 94 | else{ 95 | verts[0] = new Vector3(frame.x,frame.y,0); 96 | verts[1] = new Vector3(frame.x,frame.y+frame.width,0); 97 | verts[2] = new Vector3(frame.x+frame.height,frame.y+frame.width,0); 98 | verts[3] = new Vector3(frame.x+frame.height,frame.y,0); 99 | } 100 | 101 | uvs[0] = verts[0].TPVector3toVector2(); 102 | uvs[1] = verts[1].TPVector3toVector2(); 103 | uvs[2] = verts[2].TPVector3toVector2(); 104 | uvs[3] = verts[3].TPVector3toVector2(); 105 | 106 | for(int i = 0; i < uvs.Length; i++){ 107 | uvs[i].x /= atlasSize.x; 108 | uvs[i].y /= atlasSize.y; 109 | uvs[i].y = 1.0f - uvs[i].y; 110 | } 111 | 112 | if(rotated){ 113 | verts[3] = new Vector3(frame.x,frame.y,0); 114 | verts[0] = new Vector3(frame.x,frame.y+frame.height,0); 115 | verts[1] = new Vector3(frame.x+frame.width,frame.y+frame.height,0); 116 | verts[2] = new Vector3(frame.x+frame.width,frame.y,0); 117 | } 118 | 119 | //v-flip 120 | for(int i = 0; i < verts.Length; i++){ 121 | verts[i].y = atlasSize.y - verts[i].y; 122 | } 123 | 124 | //original origin 125 | for(int i = 0; i < verts.Length; i++){ 126 | verts[i].x -= frame.x - spriteSourceSize.x + (sourceSize.x/2.0f); 127 | verts[i].y -= (atlasSize.y - frame.y) - (sourceSize.y - spriteSourceSize.y) + (sourceSize.y/2.0f); 128 | } 129 | 130 | //scaler 131 | for(int i = 0; i < verts.Length; i++){ 132 | verts[i] *= scale; 133 | } 134 | 135 | //rotator 136 | if(rotation != Quaternion.identity){ 137 | for(int i = 0; i < verts.Length; i++){ 138 | verts[i] = rotation * verts[i]; 139 | } 140 | } 141 | 142 | for(int i = 0; i < colors.Length; i++){ 143 | colors[i] = defaultColor; 144 | } 145 | 146 | m.vertices = verts; 147 | m.uv = uvs; 148 | m.colors32 = colors; 149 | m.triangles = new int[6]{0,3,1,1,3,2}; 150 | 151 | m.RecalculateNormals(); 152 | m.RecalculateBounds(); 153 | m.name = name; 154 | 155 | return m; 156 | } 157 | 158 | public SpriteMetaData BuildBasicSprite(float scale, Color32 defaultColor){ 159 | SpriteMetaData smd = new SpriteMetaData(); 160 | Rect rect; 161 | 162 | if(!rotated){ 163 | rect = this.frame; 164 | } 165 | else 166 | { 167 | rect = new Rect(frame.x,frame.y,frame.height,frame.width); 168 | } 169 | 170 | /* Look if frame is outside from texture */ 171 | if( (frame.x + frame.width) > atlasSize.x || (frame.y + frame.height) > atlasSize.y || 172 | (frame.x < 0 || frame.y < 0)) 173 | { 174 | Debug.Log(this.name + " is outside from texture! Sprite is ignored!"); 175 | smd.name = "IGNORE_SPRITE"; 176 | return smd; 177 | } 178 | 179 | //calculate Height 180 | /* Example: Texture: 1000 Width x 500 height 181 | * Sprite.Recht(0,0,100,100) --> Sprite is on the bottom left 182 | */ 183 | rect.y = atlasSize.y - frame.y - rect.height; 184 | 185 | smd.rect = rect; 186 | smd.alignment = (int)SpriteAlignment.Center; 187 | smd.name = name; 188 | smd.pivot = new Vector2(frame.width/2, frame.height/2); 189 | 190 | return smd; 191 | } 192 | } 193 | 194 | public class MetaData{ 195 | public string image; 196 | public string format; 197 | public Vector2 size; 198 | public float scale; 199 | public string smartUpdate; 200 | 201 | public MetaData(Hashtable table){ 202 | image = (string)table["image"]; 203 | format = (string)table["format"]; 204 | size = ((Hashtable)table["size"]).TPHashtableToVector2(); 205 | scale = float.Parse(table["scale"].ToString()); 206 | smartUpdate = (string)table["smartUpdate"]; 207 | } 208 | } 209 | 210 | public static List ProcessToSprites(string text) { 211 | Hashtable table = text.hashtableFromJson(); 212 | MetaData meta = new MetaData((Hashtable)table["meta"]); 213 | Hashtable frameTable = (Hashtable)table["frames"]; 214 | 215 | List frames = new List(); 216 | foreach(DictionaryEntry entry in frameTable) { 217 | frames.Add(new PackedFrame((string)entry.Key, meta.size, (Hashtable)entry.Value)); 218 | } 219 | alphabatizeFramesByName( frames ); 220 | 221 | List sprites = new List(); 222 | for(int i = 0; i < frames.Count; i++){ 223 | SpriteMetaData smd = frames[i].BuildBasicSprite( 0.01f, new Color32(128,128,128,128)); 224 | if(smd.name.Equals("IGNORE_SPRITE")) { 225 | continue; 226 | } 227 | sprites.Add(smd); 228 | } 229 | return sprites; 230 | } 231 | 232 | static void alphabatizeFramesByName(List frames) 233 | { 234 | frames.Sort(delegate(PackedFrame a, PackedFrame b) { 235 | if (a.name == null && b.name == null) { 236 | return 0; 237 | }else if (a.name == null) { 238 | return -1; 239 | }else if (b.name == null) { 240 | return 1; 241 | }else { 242 | return a.name.CompareTo(b.name); 243 | } 244 | }); 245 | } 246 | 247 | public static Mesh[] ProcessToMeshes(string text){ 248 | return ProcessToMeshes(text, Quaternion.identity); 249 | } 250 | 251 | public static Mesh[] ProcessToMeshes(string text, Quaternion rotation){ 252 | Hashtable table = text.hashtableFromJson(); 253 | 254 | MetaData meta = new MetaData((Hashtable)table["meta"]); 255 | 256 | List frames = new List(); 257 | Hashtable frameTable = (Hashtable)table["frames"]; 258 | 259 | foreach(DictionaryEntry entry in frameTable){ 260 | frames.Add(new PackedFrame((string)entry.Key, meta.size, (Hashtable)entry.Value)); 261 | } 262 | 263 | List meshes = new List(); 264 | for(int i = 0; i < frames.Count; i++){ 265 | meshes.Add(frames[i].BuildBasicMesh(0.01f, new Color32(128,128,128,128), rotation)); 266 | } 267 | 268 | return meshes.ToArray(); 269 | } 270 | 271 | public static MetaData GetMetaData(string text){ 272 | Hashtable table = text.hashtableFromJson(); 273 | MetaData meta = new MetaData((Hashtable)table["meta"]); 274 | 275 | return meta; 276 | } 277 | } 278 | 279 | #endif 280 | -------------------------------------------------------------------------------- /TexturePacker/Editor/TexturePackerImport.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Mitch Thompson 3 | Extended by Harald Lurger (2013) (Process to Sprites) 4 | 5 | Standard MIT License 6 | 7 | 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: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | 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. 12 | */ 13 | 14 | using UnityEngine; 15 | using UnityEditor; 16 | using System.Collections; 17 | using System.Collections.Generic; 18 | using System.Text; 19 | using System.IO; 20 | 21 | public static class TexturePackerImport{ 22 | 23 | [MenuItem("Assets/TexturePacker/Process to Sprites")] 24 | static void ProcessToSprite(){ 25 | TextAsset txt = (TextAsset)Selection.activeObject; 26 | 27 | string rootPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(txt)); 28 | TexturePacker.MetaData meta = TexturePacker.GetMetaData(txt.text); 29 | 30 | List sprites = TexturePacker.ProcessToSprites(txt.text); 31 | 32 | string path = rootPath + "/" + meta.image; 33 | 34 | sprites = MaintainSpriteOrder(path, sprites, meta.image); 35 | 36 | TextureImporter texImp = AssetImporter.GetAtPath(path) as TextureImporter; 37 | texImp.spritesheet = sprites.ToArray(); 38 | texImp.textureType = TextureImporterType.Sprite; 39 | texImp.spriteImportMode = SpriteImportMode.Multiple; 40 | 41 | AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate ); 42 | } 43 | 44 | static List MaintainSpriteOrder(string path, List sprites, string imageName) 45 | { 46 | sprites = new List(sprites); // Work on a copy, method shouldn't have side-effects 47 | List orderedSprites = new List(); 48 | 49 | Object[] existing = AssetDatabase.LoadAllAssetsAtPath(path); 50 | 51 | for (int i = 0; i < existing.Length; ++i) 52 | { 53 | Sprite existingSprite = existing[i] as Sprite; 54 | 55 | if (null != existingSprite) 56 | { 57 | int pickedIndex = FindSpriteFromListByName(sprites, existingSprite.name); 58 | if (-1 != pickedIndex) 59 | { 60 | orderedSprites.Add(sprites[pickedIndex]); 61 | sprites.RemoveAt(pickedIndex); 62 | } 63 | else 64 | { 65 | if (existingSprite.name != imageName) // Make sure not to add the spritesheet image itself 66 | { 67 | SpriteMetaData placeholder = new SpriteMetaData(); 68 | placeholder.name = existingSprite.name; 69 | orderedSprites.Add(placeholder); 70 | Debug.LogWarning(existingSprite.name + " removed from spritesheet. Adding blank placeholder."); 71 | } 72 | } 73 | } 74 | else 75 | { 76 | if (null == existing[i] as Texture2D) 77 | Debug.LogWarning("Unexpected type " + existing[i]); 78 | } 79 | } 80 | 81 | orderedSprites.AddRange(sprites); 82 | 83 | return orderedSprites; 84 | } 85 | 86 | static int FindSpriteFromListByName(List spriteList, string name) 87 | { 88 | int index = -1; 89 | 90 | for (int i = 0; i < spriteList.Count; ++i) 91 | { 92 | if (spriteList[i].name == name) 93 | { 94 | index = i; 95 | break; 96 | } 97 | } 98 | 99 | return index; 100 | } 101 | 102 | [MenuItem("Assets/TexturePacker/Process to Meshes")] 103 | static Mesh[] ProcessToMeshes(){ 104 | TextAsset txt = (TextAsset)Selection.activeObject; 105 | 106 | Quaternion rotation = Quaternion.identity; 107 | string pref = EditorPrefs.GetString("TexturePackerImporterFacing", "back"); 108 | 109 | switch(pref){ 110 | case "back": 111 | rotation = Quaternion.identity; 112 | break; 113 | case "forward": 114 | rotation = Quaternion.LookRotation(Vector3.back); 115 | break; 116 | case "up": 117 | rotation = Quaternion.LookRotation(Vector3.down, Vector3.forward); 118 | break; 119 | case "down": 120 | rotation = Quaternion.LookRotation(Vector3.up, Vector3.back); 121 | break; 122 | case "right": 123 | rotation = Quaternion.LookRotation(Vector3.left); 124 | break; 125 | case "left": 126 | rotation = Quaternion.LookRotation(Vector3.right); 127 | break; 128 | } 129 | 130 | Mesh[] meshes = TexturePacker.ProcessToMeshes(txt.text, rotation); 131 | 132 | string rootPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(txt)); 133 | 134 | Directory.CreateDirectory(Application.dataPath + "/" + rootPath.Substring(7, rootPath.Length-7) + "/Meshes"); 135 | 136 | Mesh[] returnMeshes = new Mesh[meshes.Length]; 137 | 138 | int i = 0; 139 | foreach(Mesh m in meshes){ 140 | string assetPath = rootPath + "/Meshes/" + Path.GetFileNameWithoutExtension(m.name) + ".asset"; 141 | Mesh existingMesh = (Mesh)AssetDatabase.LoadAssetAtPath(assetPath, typeof(Mesh)); 142 | if(existingMesh == null){ 143 | AssetDatabase.CreateAsset(m, assetPath); 144 | existingMesh = (Mesh)AssetDatabase.LoadAssetAtPath(assetPath, typeof(Mesh)); 145 | } 146 | else{ 147 | existingMesh.triangles = new int[0]; 148 | existingMesh.colors32 = new Color32[0]; 149 | existingMesh.uv = new Vector2[0]; 150 | existingMesh.vertices = m.vertices; 151 | existingMesh.uv = m.uv; 152 | existingMesh.colors32 = m.colors32; 153 | existingMesh.triangles = m.triangles; 154 | existingMesh.RecalculateNormals(); 155 | existingMesh.RecalculateBounds(); 156 | EditorUtility.SetDirty(existingMesh); 157 | Mesh.DestroyImmediate(m); 158 | } 159 | 160 | returnMeshes[i] = existingMesh; 161 | i++; 162 | } 163 | 164 | return returnMeshes; 165 | } 166 | 167 | [MenuItem("Assets/TexturePacker/Process to Prefabs")] 168 | static void ProcessToPrefabs(){ 169 | Mesh[] meshes = ProcessToMeshes(); 170 | 171 | TextAsset txt = (TextAsset)Selection.activeObject; 172 | string rootPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(txt)); 173 | 174 | string prefabPath = rootPath.Substring(7, rootPath.Length-7) + "/Prefabs"; 175 | Directory.CreateDirectory(Application.dataPath + "/" + prefabPath); 176 | 177 | prefabPath = "Assets/" + prefabPath; 178 | 179 | //make material 180 | TexturePacker.MetaData meta = TexturePacker.GetMetaData(txt.text); 181 | 182 | string matPath = rootPath + "/" + (Path.GetFileNameWithoutExtension(meta.image) + ".mat"); 183 | string texturePath = rootPath + "/" + meta.image; 184 | Material mat = (Material)AssetDatabase.LoadAssetAtPath(matPath, typeof(Material)); 185 | if(mat == null){ 186 | mat = new Material(Shader.Find("Sprites/Transparent Unlit")); 187 | Texture2D tex = (Texture2D)AssetDatabase.LoadAssetAtPath(texturePath, typeof(Texture2D)); 188 | if(tex == null){ 189 | EditorUtility.DisplayDialog("Error!", "Texture " + meta.image + " not found!", "Ok"); 190 | } 191 | mat.mainTexture = tex; 192 | AssetDatabase.CreateAsset(mat, matPath); 193 | } 194 | 195 | AssetDatabase.Refresh(); 196 | 197 | for(int i = 0; i < meshes.Length; i++){ 198 | string prefabFilePath = prefabPath + "/" + meshes[i].name + ".prefab"; 199 | 200 | bool createdNewPrefab = false; 201 | Object prefab = AssetDatabase.LoadAssetAtPath(prefabFilePath, typeof(Object)); 202 | 203 | if(prefab == null){ 204 | prefab = PrefabUtility.CreateEmptyPrefab(prefabFilePath); 205 | createdNewPrefab = true; 206 | } 207 | 208 | if(createdNewPrefab){ 209 | GameObject go = new GameObject(meshes[i].name, typeof(MeshRenderer), typeof(MeshFilter)); 210 | go.GetComponent().sharedMesh = meshes[i]; 211 | go.renderer.sharedMaterial = mat; 212 | 213 | PrefabUtility.ReplacePrefab(go, prefab, ReplacePrefabOptions.ConnectToPrefab); 214 | 215 | GameObject.DestroyImmediate(go); 216 | } 217 | else{ 218 | GameObject pgo = (GameObject)prefab; 219 | pgo.renderer.sharedMaterial = mat; 220 | pgo.GetComponent().sharedMesh = meshes[i]; 221 | EditorUtility.SetDirty(pgo); 222 | } 223 | } 224 | } 225 | 226 | //Validators 227 | [MenuItem("Assets/TexturePacker/Process to Prefabs", true)] 228 | [MenuItem("Assets/TexturePacker/Process to Meshes", true)] 229 | [MenuItem("Assets/TexturePacker/Process to Sprites", true)] 230 | static bool ValidateProcessTexturePacker(){ 231 | Object o = Selection.activeObject; 232 | 233 | if(o == null) 234 | return false; 235 | 236 | if(o.GetType() == typeof(TextAsset)){ 237 | return (((TextAsset)o).text.hashtableFromJson()).IsTexturePackerTable(); 238 | } 239 | 240 | return false; 241 | } 242 | 243 | //Attach 90 degree "Shadow" meshes 244 | [MenuItem("Assets/TexturePacker/Attach Shadow Mesh")] 245 | static void AttachShadowMesh(){ 246 | List meshes = new List(); 247 | foreach(Object o in Selection.objects){ 248 | if(o is Mesh) meshes.Add(o as Mesh); 249 | } 250 | 251 | foreach(Mesh m in meshes){ 252 | Vector3[] verts = new Vector3[m.vertexCount*2]; 253 | Vector2[] uvs = new Vector2[m.vertexCount*2]; 254 | Color32[] colors = new Color32[m.vertexCount*2]; 255 | int[] triangles = new int[m.triangles.Length * 2]; 256 | 257 | System.Array.Copy(m.vertices, 0, verts, m.vertexCount, m.vertexCount); 258 | System.Array.Copy(m.uv, 0, uvs, m.vertexCount, m.vertexCount); 259 | System.Array.Copy(m.colors32, 0, colors, m.vertexCount, m.vertexCount); 260 | System.Array.Copy(m.triangles, 0, triangles, m.triangles.Length, m.triangles.Length); 261 | 262 | for(int i = 0; i < m.vertexCount; i++){ 263 | verts[i].x = verts[i+m.vertexCount].x; 264 | verts[i].y = verts[i+m.vertexCount].z; 265 | verts[i].z = verts[i+m.vertexCount].y; 266 | 267 | uvs[i] = uvs[i+m.vertexCount]; 268 | colors[i] = new Color32(0,0,0,64); 269 | 270 | 271 | } 272 | 273 | for(int i = 0; i < m.triangles.Length; i++){ 274 | triangles[i] = triangles[i + m.triangles.Length]; 275 | triangles[i + m.triangles.Length] += m.vertexCount; 276 | 277 | } 278 | 279 | m.vertices = verts; 280 | m.uv = uvs; 281 | m.colors32 = colors; 282 | m.triangles = triangles; 283 | 284 | m.RecalculateNormals(); 285 | m.RecalculateBounds(); 286 | EditorUtility.SetDirty(m); 287 | } 288 | } 289 | 290 | //Validators 291 | [MenuItem("Assets/TexturePacker/Attach Shadow Mesh", true)] 292 | static bool ValidateAttachShadowMesh(){ 293 | Object[] objs = Selection.objects; 294 | foreach(Object o in objs){ 295 | if(!(o is Mesh)){ 296 | return false; 297 | } 298 | } 299 | 300 | return true; 301 | } 302 | 303 | //Options 304 | [MenuItem("Assets/TexturePacker/Facing/Back")] 305 | static void SetFacingBack(){ EditorPrefs.SetString("TexturePackerImporterFacing", "back"); } 306 | 307 | [MenuItem("Assets/TexturePacker/Facing/Forward")] 308 | static void SetFacingForward(){ EditorPrefs.SetString("TexturePackerImporterFacing", "forward"); } 309 | 310 | [MenuItem("Assets/TexturePacker/Facing/Up")] 311 | static void SetFacingUp(){ EditorPrefs.SetString("TexturePackerImporterFacing", "up"); } 312 | 313 | [MenuItem("Assets/TexturePacker/Facing/Down")] 314 | static void SetFacingDown(){ EditorPrefs.SetString("TexturePackerImporterFacing", "down"); } 315 | 316 | [MenuItem("Assets/TexturePacker/Facing/Right")] 317 | static void SetFacingRight(){ EditorPrefs.SetString("TexturePackerImporterFacing", "right"); } 318 | 319 | [MenuItem("Assets/TexturePacker/Facing/Left")] 320 | static void SetFacingLeft(){ EditorPrefs.SetString("TexturePackerImporterFacing", "left"); } 321 | } 322 | -------------------------------------------------------------------------------- /TexturePacker/Plugins/MiniJSON.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Text; 4 | using System.Collections.Generic; 5 | using UnityEngine; 6 | 7 | /* Based on the JSON parser from 8 | * http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html 9 | * 10 | * I simplified it so that it doesn't throw exceptions 11 | * and can be used in Unity iPhone with maximum code stripping. 12 | * 13 | * Additional: 14 | * Modified by Mitch Thompson to output new-line formatting to make things more readable 15 | * Also modified to parse all numbers to float or single instead of double 16 | * Also modified to support float Infinity (defaults to Positive Infinity) 17 | */ 18 | /// 19 | /// This class encodes and decodes JSON strings. 20 | /// Spec. details, see http://www.json.org/ 21 | /// 22 | /// JSON uses Arrays and Objects. These correspond here to the datatypes ArrayList and Hashtable. 23 | /// All numbers are parsed to doubles. 24 | /// 25 | public class MiniJSON 26 | { 27 | private const int TOKEN_NONE = 0; 28 | private const int TOKEN_CURLY_OPEN = 1; 29 | private const int TOKEN_CURLY_CLOSE = 2; 30 | private const int TOKEN_SQUARED_OPEN = 3; 31 | private const int TOKEN_SQUARED_CLOSE = 4; 32 | private const int TOKEN_COLON = 5; 33 | private const int TOKEN_COMMA = 6; 34 | private const int TOKEN_STRING = 7; 35 | private const int TOKEN_NUMBER = 8; 36 | private const int TOKEN_TRUE = 9; 37 | private const int TOKEN_FALSE = 10; 38 | private const int TOKEN_NULL = 11; 39 | //added token for infinity 40 | private const int TOKEN_INFINITY = 12; 41 | private const int BUILDER_CAPACITY = 2000; 42 | 43 | /// 44 | /// On decoding, this value holds the position at which the parse failed (-1 = no error). 45 | /// 46 | protected static int lastErrorIndex = -1; 47 | protected static string lastDecode = ""; 48 | 49 | 50 | /// 51 | /// Parses the string json into a value 52 | /// 53 | /// A JSON string. 54 | /// An ArrayList, a Hashtable, a double, a string, null, true, or false 55 | public static object jsonDecode( string json ) 56 | { 57 | // save the string for debug information 58 | MiniJSON.lastDecode = json; 59 | 60 | if( json != null ) 61 | { 62 | char[] charArray = json.ToCharArray(); 63 | int index = 0; 64 | bool success = true; 65 | object value = MiniJSON.parseValue( charArray, ref index, ref success ); 66 | 67 | if( success ) 68 | MiniJSON.lastErrorIndex = -1; 69 | else 70 | MiniJSON.lastErrorIndex = index; 71 | 72 | return value; 73 | } 74 | else 75 | { 76 | return null; 77 | } 78 | } 79 | 80 | 81 | /// 82 | /// Converts a Hashtable / ArrayList / Dictionary(string,string) object into a JSON string 83 | /// 84 | /// A Hashtable / ArrayList 85 | /// A JSON encoded string, or null if object 'json' is not serializable 86 | public static string jsonEncode( object json ) 87 | { 88 | indentLevel = 0; 89 | var builder = new StringBuilder( BUILDER_CAPACITY ); 90 | var success = MiniJSON.serializeValue( json, builder ); 91 | 92 | return ( success ? builder.ToString() : null ); 93 | } 94 | 95 | 96 | /// 97 | /// On decoding, this function returns the position at which the parse failed (-1 = no error). 98 | /// 99 | /// 100 | public static bool lastDecodeSuccessful() 101 | { 102 | return ( MiniJSON.lastErrorIndex == -1 ); 103 | } 104 | 105 | 106 | /// 107 | /// On decoding, this function returns the position at which the parse failed (-1 = no error). 108 | /// 109 | /// 110 | public static int getLastErrorIndex() 111 | { 112 | return MiniJSON.lastErrorIndex; 113 | } 114 | 115 | 116 | /// 117 | /// If a decoding error occurred, this function returns a piece of the JSON string 118 | /// at which the error took place. To ease debugging. 119 | /// 120 | /// 121 | public static string getLastErrorSnippet() 122 | { 123 | if( MiniJSON.lastErrorIndex == -1 ) 124 | { 125 | return ""; 126 | } 127 | else 128 | { 129 | int startIndex = MiniJSON.lastErrorIndex - 5; 130 | int endIndex = MiniJSON.lastErrorIndex + 15; 131 | if( startIndex < 0 ) 132 | startIndex = 0; 133 | 134 | if( endIndex >= MiniJSON.lastDecode.Length ) 135 | endIndex = MiniJSON.lastDecode.Length - 1; 136 | 137 | return MiniJSON.lastDecode.Substring( startIndex, endIndex - startIndex + 1 ); 138 | } 139 | } 140 | 141 | 142 | #region Parsing 143 | 144 | protected static Hashtable parseObject( char[] json, ref int index ) 145 | { 146 | Hashtable table = new Hashtable(); 147 | int token; 148 | 149 | // { 150 | nextToken( json, ref index ); 151 | 152 | bool done = false; 153 | while( !done ) 154 | { 155 | token = lookAhead( json, index ); 156 | if( token == MiniJSON.TOKEN_NONE ) 157 | { 158 | return null; 159 | } 160 | else if( token == MiniJSON.TOKEN_COMMA ) 161 | { 162 | nextToken( json, ref index ); 163 | } 164 | else if( token == MiniJSON.TOKEN_CURLY_CLOSE ) 165 | { 166 | nextToken( json, ref index ); 167 | return table; 168 | } 169 | else 170 | { 171 | // name 172 | string name = parseString( json, ref index ); 173 | if( name == null ) 174 | { 175 | return null; 176 | } 177 | 178 | // : 179 | token = nextToken( json, ref index ); 180 | if( token != MiniJSON.TOKEN_COLON ) 181 | return null; 182 | 183 | // value 184 | bool success = true; 185 | object value = parseValue( json, ref index, ref success ); 186 | if( !success ) 187 | return null; 188 | 189 | table[name] = value; 190 | } 191 | } 192 | 193 | return table; 194 | } 195 | 196 | 197 | protected static ArrayList parseArray( char[] json, ref int index ) 198 | { 199 | ArrayList array = new ArrayList(); 200 | 201 | // [ 202 | nextToken( json, ref index ); 203 | 204 | bool done = false; 205 | while( !done ) 206 | { 207 | int token = lookAhead( json, index ); 208 | if( token == MiniJSON.TOKEN_NONE ) 209 | { 210 | return null; 211 | } 212 | else if( token == MiniJSON.TOKEN_COMMA ) 213 | { 214 | nextToken( json, ref index ); 215 | } 216 | else if( token == MiniJSON.TOKEN_SQUARED_CLOSE ) 217 | { 218 | nextToken( json, ref index ); 219 | break; 220 | } 221 | else 222 | { 223 | bool success = true; 224 | object value = parseValue( json, ref index, ref success ); 225 | if( !success ) 226 | return null; 227 | 228 | array.Add( value ); 229 | } 230 | } 231 | 232 | return array; 233 | } 234 | 235 | 236 | protected static object parseValue( char[] json, ref int index, ref bool success ) 237 | { 238 | switch( lookAhead( json, index ) ) 239 | { 240 | case MiniJSON.TOKEN_STRING: 241 | return parseString( json, ref index ); 242 | case MiniJSON.TOKEN_NUMBER: 243 | return parseNumber( json, ref index ); 244 | case MiniJSON.TOKEN_CURLY_OPEN: 245 | return parseObject( json, ref index ); 246 | case MiniJSON.TOKEN_SQUARED_OPEN: 247 | return parseArray( json, ref index ); 248 | case MiniJSON.TOKEN_TRUE: 249 | nextToken( json, ref index ); 250 | return Boolean.Parse( "TRUE" ); 251 | case MiniJSON.TOKEN_FALSE: 252 | nextToken( json, ref index ); 253 | return Boolean.Parse( "FALSE" ); 254 | case MiniJSON.TOKEN_NULL: 255 | nextToken( json, ref index ); 256 | return null; 257 | case MiniJSON.TOKEN_INFINITY: 258 | nextToken( json, ref index ); 259 | return float.PositiveInfinity; 260 | case MiniJSON.TOKEN_NONE: 261 | break; 262 | } 263 | 264 | success = false; 265 | return null; 266 | } 267 | 268 | 269 | protected static string parseString( char[] json, ref int index ) 270 | { 271 | string s = ""; 272 | char c; 273 | 274 | eatWhitespace( json, ref index ); 275 | 276 | // " 277 | c = json[index++]; 278 | 279 | bool complete = false; 280 | while( !complete ) 281 | { 282 | if( index == json.Length ) 283 | break; 284 | 285 | c = json[index++]; 286 | if( c == '"' ) 287 | { 288 | complete = true; 289 | break; 290 | } 291 | else if( c == '\\' ) 292 | { 293 | if( index == json.Length ) 294 | break; 295 | 296 | c = json[index++]; 297 | if( c == '"' ) 298 | { 299 | s += '"'; 300 | } 301 | else if( c == '\\' ) 302 | { 303 | s += '\\'; 304 | } 305 | else if( c == '/' ) 306 | { 307 | s += '/'; 308 | } 309 | else if( c == 'b' ) 310 | { 311 | s += '\b'; 312 | } 313 | else if( c == 'f' ) 314 | { 315 | s += '\f'; 316 | } 317 | else if( c == 'n' ) 318 | { 319 | s += '\n'; 320 | } 321 | else if( c == 'r' ) 322 | { 323 | s += '\r'; 324 | } 325 | else if( c == 't' ) 326 | { 327 | s += '\t'; 328 | } 329 | else if( c == 'u' ) 330 | { 331 | int remainingLength = json.Length - index; 332 | if( remainingLength >= 4 ) 333 | { 334 | char[] unicodeCharArray = new char[4]; 335 | Array.Copy( json, index, unicodeCharArray, 0, 4 ); 336 | 337 | // Drop in the HTML markup for the unicode character 338 | s += "&#x" + new string( unicodeCharArray ) + ";"; 339 | 340 | /* 341 | uint codePoint = UInt32.Parse(new string(unicodeCharArray), NumberStyles.HexNumber); 342 | // convert the integer codepoint to a unicode char and add to string 343 | s += Char.ConvertFromUtf32((int)codePoint); 344 | */ 345 | 346 | // skip 4 chars 347 | index += 4; 348 | } 349 | else 350 | { 351 | break; 352 | } 353 | 354 | } 355 | } 356 | else 357 | { 358 | s += c; 359 | } 360 | 361 | } 362 | 363 | if( !complete ) 364 | return null; 365 | 366 | return s; 367 | } 368 | 369 | 370 | protected static float parseNumber( char[] json, ref int index ) 371 | { 372 | eatWhitespace( json, ref index ); 373 | 374 | int lastIndex = getLastIndexOfNumber( json, index ); 375 | int charLength = ( lastIndex - index ) + 1; 376 | char[] numberCharArray = new char[charLength]; 377 | 378 | Array.Copy( json, index, numberCharArray, 0, charLength ); 379 | index = lastIndex + 1; 380 | return float.Parse( new string( numberCharArray ) ); // , CultureInfo.InvariantCulture); 381 | } 382 | 383 | 384 | protected static int getLastIndexOfNumber( char[] json, int index ) 385 | { 386 | int lastIndex; 387 | for( lastIndex = index; lastIndex < json.Length; lastIndex++ ) 388 | if( "0123456789+-.eE".IndexOf( json[lastIndex] ) == -1 ) 389 | { 390 | break; 391 | } 392 | return lastIndex - 1; 393 | } 394 | 395 | 396 | protected static void eatWhitespace( char[] json, ref int index ) 397 | { 398 | for( ; index < json.Length; index++ ) 399 | if( " \t\n\r".IndexOf( json[index] ) == -1 ) 400 | { 401 | break; 402 | } 403 | } 404 | 405 | 406 | protected static int lookAhead( char[] json, int index ) 407 | { 408 | int saveIndex = index; 409 | return nextToken( json, ref saveIndex ); 410 | } 411 | 412 | 413 | protected static int nextToken( char[] json, ref int index ) 414 | { 415 | eatWhitespace( json, ref index ); 416 | 417 | if( index == json.Length ) 418 | { 419 | return MiniJSON.TOKEN_NONE; 420 | } 421 | 422 | char c = json[index]; 423 | index++; 424 | switch( c ) 425 | { 426 | case '{': 427 | return MiniJSON.TOKEN_CURLY_OPEN; 428 | case '}': 429 | return MiniJSON.TOKEN_CURLY_CLOSE; 430 | case '[': 431 | return MiniJSON.TOKEN_SQUARED_OPEN; 432 | case ']': 433 | return MiniJSON.TOKEN_SQUARED_CLOSE; 434 | case ',': 435 | return MiniJSON.TOKEN_COMMA; 436 | case '"': 437 | return MiniJSON.TOKEN_STRING; 438 | case '0': 439 | case '1': 440 | case '2': 441 | case '3': 442 | case '4': 443 | case '5': 444 | case '6': 445 | case '7': 446 | case '8': 447 | case '9': 448 | case '-': 449 | return MiniJSON.TOKEN_NUMBER; 450 | case ':': 451 | return MiniJSON.TOKEN_COLON; 452 | } 453 | index--; 454 | 455 | int remainingLength = json.Length - index; 456 | 457 | // Infinity 458 | if( remainingLength >= 8 ) 459 | { 460 | if( json[index] == 'I' && 461 | json[index + 1] == 'n' && 462 | json[index + 2] == 'f' && 463 | json[index + 3] == 'i' && 464 | json[index + 4] == 'n' && 465 | json[index + 5] == 'i' && 466 | json[index + 6] == 't' && 467 | json[index + 7] == 'y' ) 468 | { 469 | index += 8; 470 | return MiniJSON.TOKEN_INFINITY; 471 | } 472 | } 473 | 474 | // false 475 | if( remainingLength >= 5 ) 476 | { 477 | if( json[index] == 'f' && 478 | json[index + 1] == 'a' && 479 | json[index + 2] == 'l' && 480 | json[index + 3] == 's' && 481 | json[index + 4] == 'e' ) 482 | { 483 | index += 5; 484 | return MiniJSON.TOKEN_FALSE; 485 | } 486 | } 487 | 488 | // true 489 | if( remainingLength >= 4 ) 490 | { 491 | if( json[index] == 't' && 492 | json[index + 1] == 'r' && 493 | json[index + 2] == 'u' && 494 | json[index + 3] == 'e' ) 495 | { 496 | index += 4; 497 | return MiniJSON.TOKEN_TRUE; 498 | } 499 | } 500 | 501 | // null 502 | if( remainingLength >= 4 ) 503 | { 504 | if( json[index] == 'n' && 505 | json[index + 1] == 'u' && 506 | json[index + 2] == 'l' && 507 | json[index + 3] == 'l' ) 508 | { 509 | index += 4; 510 | return MiniJSON.TOKEN_NULL; 511 | } 512 | } 513 | 514 | return MiniJSON.TOKEN_NONE; 515 | } 516 | 517 | #endregion 518 | 519 | 520 | #region Serialization 521 | 522 | protected static bool serializeObjectOrArray( object objectOrArray, StringBuilder builder ) 523 | { 524 | if( objectOrArray is Hashtable ) 525 | { 526 | return serializeObject( (Hashtable)objectOrArray, builder ); 527 | } 528 | else if( objectOrArray is ArrayList ) 529 | { 530 | return serializeArray( (ArrayList)objectOrArray, builder ); 531 | } 532 | else 533 | { 534 | return false; 535 | } 536 | } 537 | 538 | protected static int indentLevel = 0; 539 | protected static bool serializeObject( Hashtable anObject, StringBuilder builder ) 540 | { 541 | 542 | indentLevel++; 543 | string indentString = ""; 544 | for(int i = 0; i < indentLevel; i++) 545 | indentString += "\t"; 546 | 547 | builder.Append( "{\n" + indentString ); 548 | 549 | 550 | 551 | IDictionaryEnumerator e = anObject.GetEnumerator(); 552 | bool first = true; 553 | while( e.MoveNext() ) 554 | { 555 | string key = e.Key.ToString(); 556 | object value = e.Value; 557 | 558 | if( !first ) 559 | { 560 | builder.Append( ", \n" + indentString ); 561 | } 562 | 563 | serializeString( key, builder ); 564 | builder.Append( ":" ); 565 | if( !serializeValue( value, builder ) ) 566 | { 567 | return false; 568 | } 569 | 570 | first = false; 571 | } 572 | 573 | 574 | indentString = ""; 575 | for(int i = 0; i < indentLevel-1; i++) 576 | indentString += "\t"; 577 | 578 | builder.Append( "\n" + indentString + "}"); 579 | 580 | indentLevel--; 581 | return true; 582 | } 583 | 584 | 585 | protected static bool serializeDictionary( Dictionary dict, StringBuilder builder ) 586 | { 587 | builder.Append( "{" ); 588 | 589 | bool first = true; 590 | foreach( var kv in dict ) 591 | { 592 | if( !first ) 593 | builder.Append( ", " ); 594 | 595 | serializeString( kv.Key, builder ); 596 | builder.Append( ":" ); 597 | serializeString( kv.Value, builder ); 598 | 599 | first = false; 600 | } 601 | 602 | builder.Append( "}" ); 603 | return true; 604 | } 605 | 606 | 607 | protected static bool serializeArray( ArrayList anArray, StringBuilder builder ) 608 | { 609 | indentLevel++; 610 | string indentString = ""; 611 | for(int i = 0; i < indentLevel; i++) 612 | indentString += "\t"; 613 | 614 | builder.Append( "[\n" + indentString ); 615 | // builder.Append( "[" ); 616 | 617 | 618 | 619 | 620 | 621 | bool first = true; 622 | for( int i = 0; i < anArray.Count; i++ ) 623 | { 624 | object value = anArray[i]; 625 | 626 | if( !first ) 627 | { 628 | builder.Append( ", \n" + indentString ); 629 | } 630 | 631 | if( !serializeValue( value, builder ) ) 632 | { 633 | return false; 634 | } 635 | 636 | first = false; 637 | } 638 | 639 | 640 | 641 | indentString = ""; 642 | for(int i = 0; i < indentLevel-1; i++) 643 | indentString += "\t"; 644 | 645 | builder.Append( "\n" + indentString + "]"); 646 | 647 | indentLevel--; 648 | 649 | return true; 650 | } 651 | 652 | 653 | protected static bool serializeValue( object value, StringBuilder builder ) 654 | { 655 | // Type t = value.GetType(); 656 | // Debug.Log("type: " + t.ToString() + " isArray: " + t.IsArray); 657 | 658 | if( value == null ) 659 | { 660 | builder.Append( "null" ); 661 | } 662 | else if( value.GetType().IsArray ) 663 | { 664 | serializeArray( new ArrayList( (ICollection)value ), builder ); 665 | } 666 | else if( value is string ) 667 | { 668 | serializeString( (string)value, builder ); 669 | } 670 | else if( value is Char ) 671 | { 672 | serializeString( Convert.ToString( (char)value ), builder ); 673 | } 674 | else if( value is Hashtable ) 675 | { 676 | serializeObject( (Hashtable)value, builder ); 677 | } 678 | else if( value is Dictionary ) 679 | { 680 | serializeDictionary( (Dictionary)value, builder ); 681 | } 682 | else if( value is ArrayList ) 683 | { 684 | serializeArray( (ArrayList)value, builder ); 685 | } 686 | else if( ( value is Boolean ) && ( (Boolean)value == true ) ) 687 | { 688 | builder.Append( "true" ); 689 | } 690 | else if( ( value is Boolean ) && ( (Boolean)value == false ) ) 691 | { 692 | builder.Append( "false" ); 693 | } 694 | else if( value.GetType().IsPrimitive ) 695 | { 696 | serializeNumber( Convert.ToSingle( value ), builder ); 697 | } 698 | else 699 | { 700 | return false; 701 | } 702 | 703 | return true; 704 | } 705 | 706 | 707 | protected static void serializeString( string aString, StringBuilder builder ) 708 | { 709 | builder.Append( "\"" ); 710 | 711 | char[] charArray = aString.ToCharArray(); 712 | for( int i = 0; i < charArray.Length; i++ ) 713 | { 714 | char c = charArray[i]; 715 | if( c == '"' ) 716 | { 717 | builder.Append( "\\\"" ); 718 | } 719 | else if( c == '\\' ) 720 | { 721 | builder.Append( "\\\\" ); 722 | } 723 | else if( c == '\b' ) 724 | { 725 | builder.Append( "\\b" ); 726 | } 727 | else if( c == '\f' ) 728 | { 729 | builder.Append( "\\f" ); 730 | } 731 | else if( c == '\n' ) 732 | { 733 | builder.Append( "\\n" ); 734 | } 735 | else if( c == '\r' ) 736 | { 737 | builder.Append( "\\r" ); 738 | } 739 | else if( c == '\t' ) 740 | { 741 | builder.Append( "\\t" ); 742 | } 743 | else 744 | { 745 | int codepoint = Convert.ToInt32( c ); 746 | if( ( codepoint >= 32 ) && ( codepoint <= 126 ) ) 747 | { 748 | builder.Append( c ); 749 | } 750 | else 751 | { 752 | builder.Append( "\\u" + Convert.ToString( codepoint, 16 ).PadLeft( 4, '0' ) ); 753 | } 754 | } 755 | } 756 | 757 | builder.Append( "\"" ); 758 | } 759 | 760 | 761 | protected static void serializeNumber( float number, StringBuilder builder ) 762 | { 763 | builder.Append( Convert.ToString( number ) ); // , CultureInfo.InvariantCulture)); 764 | } 765 | 766 | #endregion 767 | 768 | } 769 | 770 | 771 | 772 | #region Extension methods 773 | 774 | public static class MiniJsonExtensions 775 | { 776 | public static string toJson( this Hashtable obj ) 777 | { 778 | return MiniJSON.jsonEncode( obj ); 779 | } 780 | 781 | public static string toJson( this Dictionary obj ) 782 | { 783 | return MiniJSON.jsonEncode( obj ); 784 | } 785 | 786 | 787 | public static ArrayList arrayListFromJson( this string json ) 788 | { 789 | return MiniJSON.jsonDecode( json ) as ArrayList; 790 | } 791 | 792 | 793 | public static Hashtable hashtableFromJson( this string json ) 794 | { 795 | return MiniJSON.jsonDecode( json ) as Hashtable; 796 | } 797 | } 798 | 799 | #endregion 800 | --------------------------------------------------------------------------------