This project was created for my simple games. It is free to use—no attribution required. Just run the installer. If all goes well, reload it and it should work. If you find any bugs, report them to me and I will try my best to fix them.
@UberGoober provided many excellent suggestions that really improved this project
Additional thanks goes to @Briarfox, @DayLightTimer, @LightDye, @Andrew_Stacey, and @Herwig for their excellent libraries.
Hey, out of interest did you write this all separately in another language and just send a request for this code through codea? I’m a complete noob and just randomly stumbled across this trying to get an idea of what’s written
Actually, no. I created this from scratch. The installer was created by AutoGist, and I used several libraries available on the wiki.
This is super excellent!
One design comment and a couple bugs:
Design-wise, is there a reason you chose to make the grid size independent of the static background of gray-and-white checkered boxes? That becomes quite distracting, for me, while drawing, and it would make it much more convenient and intuitive to use the checker boxes themselves as the scalable grid, and remove the option for a separate grid all together.
Two bugs (or one, depending how you look at it):
The export and brush resizing tools don’t seem to look like they’re supposed to. I am using an old iPad and running iOS 9.x, so maybe it’s just a matter of not supporting that hardware and system?
What they look like for me is a black box with a white outline that doesn’t seem to be placed in a relevant position, and then other controls. For the brush resizer, the slider tool seems to be in the wrong place, but it works nicely. For exporting things, there seems to be a white text field, default text, the aforementioned black square with white outline, and a green button reading “expert”, none of which seem to be aligned with each other. They are kind of all over the place, and none of them responded to tapping.
But overall really really nice!
Also I fully dig that you included a custom icon. Rockin’!
Ugh. The problem was that my UI control positions were calibrated in
setup(). They don’t change with the screen size when you rotate the device or open/close the sidebar. They should stay nicely positioned if I lock rotation and set
displayMode to full screen. I hope my fix works (I’ll update the installer in my main post; just run it again).
@UberGoober for the checkered boxes, yes I agree that they are very distracting, but I didn’t understand your explanation on how to fix it. I also think they seem a little bit too light-bluish—could you suggest some better colors? They could also be eliminated altogether, leaving just the typical dark blue that Codea uses by default for the background.
@em2 This is a thing Codea could really use in general, so I think it’s a great effort on your part to share it and to work to improve it.
- Did you post the updated code? I re-installed and still had all the same bugs.
- It seems to start in zoom mode. This is a little confusing.
- It’s an elegant approach to make the color picker the same as the “draw” button, but it is a little confusing, because when I want to draw I look for a draw icon or button: it’s not intuitive to go for a button marked “picker”. If you ask me you should either put a draw button right next to the erase button or next to the color picker button. If you want to retain elegance you could make the eye dropper button a part of the color picker. When you tap “color picker” the current picker would appear, but on that sub-window there’d be a button marked “eye dropper”, that activates the eye dropper tool. Thematically this makes sense because they’re both forms of color selection.
- The eye dropper tool is really cool.
- Can you make the eye dropper by default ignore grid lines, so the user never accidentally picks up white?
- Re: grid and checkerboard. Imho you need both, the grid for positioning and the checkerboard for denoting transparency.
- The checkerboard has to align with the grid lines. This is what all professional drawing software does. Usually they employ a scaling algorithm that allows the checkerboard to have bigger squares the more you zoom out, but the squares themselves always align with pixel boundaries. Because this project is all about pixel art, the user won’t ever zoom out far enough to require such an algorithm, but the point is the same: if you’re going to employ a checkerboard, it must align with the pixel boundaries of the art that’s being drawn–which in this case means it also must align with the grid.
- On my ancient hardware there is one and sometimes two seconds of lag between dragging my finger across the canvas and seeing the result on the screen. It’s enough that I’m often not sure if the program has crashed or not.
- Fixing the lag itself is obviously ideal, but if that’s impossible, some sort of visual indication that the input has been received is necessary, like an hourglass or a spinny ball or what have you. You could even temporarily put a gray overlay on the whole screen for an easy fix. Or something else that you think of…
Again this is a really cool project and I hope my detailed critique doesn’t come off as disparagement. It’s quite an achievement imho.
Criticism should not be taken as disparagement; it should be received and applied, regardless of the motive of the critic to help improve oneself, so don’t worry about me worrying ;). I admire your sense of taste and wish I had it in myself to notice the small things that make user experience feel more natural. Maybe I should ask for some advice for a new game I am working on too.
Release 1.0.5 changelog (merged from suggestions by @UberGoober):
- Checkered background now moves with zoom
- Zoom button changes to a pencil icon on tap
- Zoom is disabled initially
- Areas beyond the checkered “transparent” area are colored a very very dark blue.
- Checkered background is not bluish anymore
- Grid lines disappear when the eyedropper is activated
- Weird placement bug fixes
- Possibly add a loading icon state or at least speed up drawing time on old hardware
Feel free to change the original code—it’s open source. I can merge your code in.
Nice! All the UI seems to show up correctly now.
Some export notes:
- The export button’s placement is problematic. It is hard to tap it without exiting or restarting the project, because it positioned in the same place as those controls.
- Also I it took many tries for me to figure out how to export any art. I kept getting that “please enter a valid Sprite name” message. I didn’t realize you had to enter the path too.
- IMHO you should lose the path requirement and just assume that all exports are going to the “Project” Sprite pack. Codea itself has a pretty elegant UI for moving art between projects, so letting the user specify a path name duplicates functionality already available elsewhere. Plus many people don’t even know their project’s individual Sprite pack is titled “Project”.
I hadn’t looked around the code before–I like your decomposition in your ‘setup()’ function, making the whole project boil down to just a few simple commands. I just started taking the free Stanford Programming Methodology course available on iTunes, and decomposition is one of the first things they over, so it’s cool to see it here.
One other thing:
- the brush size tool preview uses a smooth round circle which is impossible to mentally transpose into big blocky pixels. I have no idea how many pixels wide and tall the biggest setting is, for example. If you replaced the smooth circle with an actual blocky-pixel preview of the brush, it would be much easier to adjust my brush to the right size.
Bug: set brush size to any big amount of size, and brush along the left edge. It will say
Main:205: attempt to index a nil value (field '?')
Main:205: in upvalue '_touched'
Better:45: in function 'touched'
Looking at that block, the problem is grid.colors[i] is a nil value. Or more specifically, you create grid using a for i=1,100 loop, but in the touched function you specically say,
local i,j = (i-1),j-1
So, at line 205, it tries to do grid.colors[i][j], but i = 0, so it can’t do anything because grid.colors is not existing
for i,r in ipairs(grid) do
for j,index in ipairs(r) do
local i,j = (i-1),j-1
local x,y = i*cc, j*cc
--[[if t.x > i*cc-hcc and t.x < i*cc+hcc and t.y > j*cc-hcc and t.y < j*cc+hcc then
-- New version w/ brush size
local dist2 = (t.x - x)*(t.x - x) + (t.y - y)*(t.y - y)
-- No square root for optimization
if dist2 < brushSize2 then
grid.colors[i][j] = pickerColor
The block that bugged out
Patch it with:
if dist2 < brushSize2 and grid.colors[i] and grid.colors[i][j] then
grid.colors[i][j] = pickerColor
In the block that bugged out. I’ll fix it in the next release (kinda busy right now).
Pixel Art 1.0.6
* Simpler file saving (an advanced button allows for the old mode)
* Better brush preview
* `FULLSCREEN_NO_BUTTONS` mode
* Reset button
I like how advanced mode gives you instructions on the correct format for a file name that includes a path.
Full screen no buttons mode and a “leave” button are a great simple solution to that problem.
That brush preview is excellent now–better than I was imagining because you actually included the grid lines.
And the Reset button is so handy!
One odd bug: the eye dropper tool never seems to work on the first tap for me. It seems to only work on the second tap.
And a design question: is there a reason you don’t use rounded corners on the export box and the brush-size box? You use rounded corners everywhere else and it looks pretty good.
@TokOut the icon is loaded from a Dropbox url and saved to
@UberGoober little design neglections like that are typically due to laziness ;). Rounded corners on corner elements are ambiguous.
saveImage("Project:Icon", I got), where
img is the Data saves the icon, right? Thanks!
@TokOut yes, something like that. On first run, the project will download an icon from dropbox and save it to the project. The actual code is below (with comments for clarification):
-- In Main
downloadImageToProject("Icon","https://www.dropbox.com/s/3t91x976chachgg/Icon%402x.png?dl=1") -- download image to Project:Icon
-- In downloadImageToProject
if not readImage("Project:"..imageName) then -- only download if image doesn't exist
print("Attempting to download "..imageName.."...")
http.request(source, -- request image
function(img,status,headers) -- success
saveImage("Project:"..imageName,img) -- save it to project with imageName
end, function(err) -- fail
Failed to get image. Your project may not work properly.")
I recently needed this for one of my games, and tweaked it quite a bit.
Pixel Art 1.0.7
- Images are now auto cropped and saved as small pixel sprites. To display them, call
- Export has an incorporated preview
- Recents pallete shows previously picked colors
As usual, comments and suggestions are welcome and desired.