SKTexture nearest filtering mode doesn't work (Making Pixel Art)
UPDATE:
SKTexture nearest filtering mode is now working in Xcode 7 GM. No need to manually scale images as I've suggested below.
I am experiencing this same problem in Xcode 7 beta 5 with beta iOS 9 ; the Nearest filtering mode is seemingly ignored. In my project, scaling with nearest-neighbor scaling was previously working in Xcode 6 on iOS 8.
In the event that the bug is not resolved before the final release of iOS 9 I am pre-scaling images before I place them in their respective atlases.
To do this I wrote a simple python script to recursively find png files and scale them using imagmagick.
If you don’t have imagemagick installed, you can install it using macports like so:
sudo port install ImageMagick
If you have homebrew it looks like this:
brew install imagemagick
I just place this script (below) in a file called imgscaler.py within the directory above my atlas files (which I want to scale by 400% in my case) and kick it off from the terminal:
python imgscaler.py
The script looks like this:
import subprocess
import os
import sys
import fnmatch
def main():
execution_folder_name = os.getcwd()
file_extension_name = "png"
#
# Collect names of files that will be manipulated
matches = []
for root, dirNames, fileNames in os.walk(execution_folder_name):
for filename in fnmatch.filter(fileNames, '*.' + file_extension_name):
full_name = os.path.join(root, filename)
matches.append(full_name)
scale_percentage = "400"
if not __query_yes_no("This script will scale images by " + scale_percentage + "% recursively from directory " + execution_folder_name + ". Proceed?"):
return
#
# Scale the images
for match in matches:
execution_str = "convert " + match + " -interpolate Nearest -filter point -resize " + scale_percentage + "% " + match + "\n"
sys.stdout.write(execution_str)
__run_command(execution_str)
def __run_command(input_cmd):
"""Runs a command on the terminal, and returns the output in a string array.
"""
process = subprocess.Popen(input_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
output = []
while True:
line = process.stdout.readline()
if line != '':
output.append(line)
else:
break
return output
def __query_yes_no(question, default="yes"):
"""Asks a yes or no question, returns a true is answered "yes".
"""
valid = {"yes": True, "y": True, "ye": True,
"no": False, "n": False}
if default is None:
prompt = " [y/n] "
elif default == "yes":
prompt = " [Y/n] "
elif default == "no":
prompt = " [y/N] "
else:
raise ValueError("invalid default answer: '%s'" % default)
while True:
sys.stdout.write(question + prompt)
sys.stdout.flush()
choice = raw_input().lower()
if default is not None and choice == '':
sys.stdout.write(default)
sys.stdout.write('\n')
sys.stdout.flush()
return valid[default]
elif choice in valid:
sys.stdout.write(choice)
sys.stdout.write('\n')
sys.stdout.flush()
return valid[choice]
else:
sys.stdout.write("Please respond with 'yes' or 'no' (or 'y' or 'n').\n")
sys.stdout.flush()
main()
Change scale_percentage
to whatever percentage you want to scale by.
BTW, I am guessing this scaling issue will eventually get resolved. I’m currently updating my code with this assumption in mind. This is just a bandaid in the event the fix to nearest-neighbor scaling in SpriteKit arrives later than iOS 9.0.
How to use pixel art in an app?
I've solved the problem...but its really a hack. I have a SKScene
which is the parent node to all of the "trees" (SKSpriteNodes
). This scene will be adding multiple trees to itself. At first I thought that this was some sort of problem because if I only added one tree, it would display the image correctly. The answer to this question led me to believe that I would need to programmatically create a SKTextureAtlas
singleton in the (the texture is in a SKTextureAtlas
) and pass it to the tree class to get the texture from on an init method. I made a property in the SKScene
to hold the texture atlas so that I could pass it to the tree class every time I made a new one. I tried loading the texture from texture atlas (in the tree class) using the textureNamed: method. This still did not work. I switched back to loading the texture with SKTexture's textureWithImageNamed: method and it worked. Further more I changed to code back so that the tree subclass would not be sent the SKTextureAtlas
singleton at all and it still worked.
In the SKScene
I get the texture atlas using:
[SKTextureAtlas atlasNamed:@"Textures"]; //Textures is the atlas name.
and set the return value to be the SKTextureAtlas
property described above. I thought that maybe the atlas just had to initialized at some point in the code, so I tried this:
SKTextureAtlas *myAtlas = [SKTextureAtlas atlasNamed:@"Textures"];
and the following alone on one line:
[SKTextureAtlas atlasNamed:@"Textures"]
but neither worked. Apparently I need to have a property in my tree's parent class which is the SKTextureAtlas
which holds the texture which the tree uses without any reference to a SKTextureAtlas
whatsoever... Is this a glitch or something? It's working now but it feels like a hack.
Related Topics
Swift Generics Protocols: Can Only Be Used as a Generic Constraint Problem
Adopting Customnserror in Decodingerror
Realmswift Initialize List:Cannot Specialize a Non-Generic Definition
Xcode Generated Coredata Files Cannot Be Processed by Copy Bundle Resources Build Phase
Distributednotificationcenter - How to Pass Data Between Applications
Swift Vscode Class in Other File Not Recognized
Call Function When If Value in Nsuserdefaults.Standarduserdefaults() Changes
How to Make a Function Complete Before Calling Others in an Ibaction
Avcapturevideopreviewlayer Is Not Visible on The Screenshot
Convert Half Precision Float (Bytes) to Float in Swift
Ar with iOS: Putting a Light in the Scene Makes Everything Black
Swiftui - Why Does the Keyboard Pushes My View
Currying in Swift, New Declaration Syntax in Future
How to Get the Scnvector3 Position of the Camera in Relation to It's Direction Arkit Swift
Any or a Trouble with Sequence of Functions
How to Convert a Computed Value to a Literal for Enum Initialization