Xprite Pixel Art Editor
Simplify your pixel art workflow.
Xprite is a pixel art editor designed for artists, game designers and programmers.
Xprite is under heavy development. Currently, a beta build is available for a discount. Don't miss out while it's still cheap :)
More info >>
Xprite
Xprite is a featureful pixel art editor written in Rust.
Features
Autoshade
Easily color your contour to achieve 3d light effect.
Symmetry
Create symmetric patterns with fine-grained control.
Also, Xprite supports rotational symmetry.
Eight-way connected floodfill
This allows recoloring thin lines.
Texture Generator
Uses wave function collapse algorithm to automatically generate intricate pixel patterns.
Outline tool
Outline your color blobs to achieve a cartoonish effect.
Bezier Curves
Draw smooth curves by sorting monotonic curves by segment slope.
Layer Exporter
Save each layer to a separate file.
Available formats: PNG, JPG, Twitter PNG, Aseprite, PSD.
pyckitup
Python game engine for the Web.
Fork me on GitHubAbout
Hi there! pyckitup is a Python game engine you can use to make 2D games. It is free, open source and works on Web, Linux, OS X and Windows.
Getting Started
- Download pyckitup binary.
-
Linux: download
-
Windows: download
-
OS X: Currently unavailable, build instructions
- Initialize game folder
The folder contains the clock example game.
pyckitup init hello
cd hello
pyckitup
-
Iterate on your game
-
Once ready, deploy to web with
pyckitup build
This creates a build/
directory which contains everything you need to deploy your awesome game to the web. Simply copy the folder to where you want it served.
How it works
pyckitup is a thin layer glueing RustPython interpreter to quicksilver game engine. When you load a pyckitup game in browser, it loads a single 5MB wasm blob and interprets Python code stored in localStorage. As a result there is a 10MB code size limit.
Fork me on GitHubExample: Clock
Source code:
import qs
from common import *
def init():
qs.init_sounds([ ["click", "click.wav"] ])
return {
"elapsed": 0,
"hours": 0,
"minutes": 0,
"seconds": 0,
}
def onload(_):
qs.set_update_rate(1000)
def update(state):
state["elapsed"] += qs.update_rate()
elapsed = state["elapsed"]
state["seconds"] = (elapsed / 1000.) % 60.
state["minutes"] = ((elapsed / 1000.) / 60.) % 60.
state["hours"] = ((elapsed / 1000.) / 60. / 24.) % 24.
qs.sound("click")
def draw(state):
qs.clear(WHITE)
# draw the frame
qs.circ([400, 300], 203, color=BLACK)
qs.circ([400, 300], 200, color=WHITE)
# draw the hour markers
for i in range(1, 13):
angle = 360. * ((i + 9.) * 2. / 24.)
rad = angle * 3.14 / 180
pos = [ math.sin(rad) * 200. + 400., math.cos(rad) * 200. + 300. ]
qs.line([[400, 300], pos], thickness=5, color=BLACK)
qs.circ([400, 300], 180, color=WHITE)
hour_angle = 360. * ((state["hours"] + 9.) * 2. / 24.) * 3.14 / 180
minute_angle = 360. * ((state["minutes"] + 45.) / 60.) * 3.14 / 180
second_angle = 360. * ((state["seconds"] + 45.) / 60.) * 3.14 / 180
hour_pos = [math.cos(hour_angle ) * 150. + 400, math.sin(hour_angle)* 150 + 300]
min_pos = [math.cos(minute_angle) * 180. + 400, math.sin(minute_angle) * 180+ 300]
second_pos = [math.cos(second_angle) * 180. + 400, math.sin(second_angle) * 180. + 300]
qs.line([[400, 300], hour_pos], thickness=10, color=BLACK)
qs.line([[400, 300], min_pos], thickness=5, color=BLUE)
qs.line([[400, 300], second_pos], thickness=3, color=RED)
Fork me on GitHub
Example: Color
Source code:
import qs
from common import *
def init():
qs.init_anims([
# name, path, nframes, duration(s)
["crab-up", "crab-up.png", 2, 1.],
])
return {
"p0": [1., 1.],
"p1": [100., 100.],
"color": [0,0,0,1],
}
def update(state):
state["p0"][0] += 0.3
state["p0"][1] += 0.3
state["color"][0] += 0.001
state["color"][1] += 0.002
state["color"][2] += 0.003
state["color"][0] %= 1
state["color"][1] %= 1
state["color"][2] %= 1
def draw(state):
qs.clear(state["color"])
qs.anim("crab-up", rect=[state["p0"], state["p1"]])
def event(state, event):
if event["event"] == "mouse_moved":
state["p0"][0] = event["x"]
state["p0"][1] = event["y"]
Fork me on GitHub
API
Lifecycle functions
The lifecycle of a pyckitup game consists of these functions:
def init() -> State
Initializes assets, returns a state object which can be an integer, a list, a dictionary or practically any Python object.
def onload(State) -> None
This function is run exactly once after window creation. Can be used for setting frame rate, getting window size, etc..
def update(State) -> None
This function is guaranteeed to run once per frame.
def draw(State) -> None
Draw your game visuals here. In contrast to update function above, there is no once-per-frame guarantee.
def event(State, Event) -> None
Event is a dictionary containing the event type and its associated data.
Fork me on GitHubAssets
All assets must be placed in the static/
folder. Assets may include sprites, sprite animations and sounds. Assets must be declared in the init lifecycle function before use. asset loading is asynchronous and pyckitup will panic if not all assets are loaded.
The asset initializer functions are detailed in the qs module section.
Directory Structure
/game
|-run.py
|-module.py
|-/static <- place your assets here
| |-sprite.png
| |-anim.png
| |-sound.wav
Fork me on GitHub
Events
Event objects are dispatched in the event
lifecycle function:
def event(state, event):
print(event)
- Closed
{
"event": "closed"
}
- Focused
{
"event": "focused"
}
- Unfocused
{
"event": "unfocused"
}
- Key
{
"event": "key",
"key": "A",
"state": "Pressed"
}
- Typed
{
"event": "typed",
"char": "A"
}
- MouseMoved
{
"event": "mouse_moved",
"x": 0,
"y": 0
}
- MouseEntered
{
"event": "mouse_entered"
}
- MouseExited
{
"event": "mouse_exited"
}
- MouseWheel
{
"event": "mouse_wheel",
"x": 0,
"y": 0
}
- MouseButton
{
"event": "mouse_button",
"button": "Left",
"state": "Pressed"
}
Buttons
Key1
Key2
Key3
Key4
Key5
Key6
Key7
Key8
Key9
Key0
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z
Escape
F1
F2
F3
F4
F5
F6
F7
F8
F9
F10
F11
F12
F13
F14
F15
F16
F17
F18
F19
F20
F21
F22
F23
F24
Snapshot
Scroll
Pause
Insert
Home
Delete
End
PageDown
PageUp
Left
Up
Right
Down
Back
Return
Space
Compose
Caret
Numlock
Numpad0
Numpad1
Numpad2
Numpad3
Numpad4
Numpad5
Numpad6
Numpad7
Numpad8
Numpad9
AbntC1
AbntC2
Add
Apostrophe
Apps
At
Ax
Backslash
Calculator
Capital
Colon
Comma
Convert
Decimal
Divide
Equals
Grave
Kana
Kanji
LAlt
LBracket
LControl
LShift
LWin
Mail
MediaSelect
MediaStop
Minus
Multiply
Mute
MyComputer
NavigateForward
NavigateBackward
NextTrack
NoConvert
NumpadComma
NumpadEnter
NumpadEquals
OEM102
Period
PlayPause
Power
PrevTrack
RAlt
RBracket
RControl
RShift
RWin
Semicolon
Slash
Sleep
Stop
Subtract
Sysrq
Tab
Underline
Unlabeled
VolumeDown
VolumeUp
Wake
WebBack
WebFavorites
WebForward
WebHome
WebRefresh
WebSearch
WebStop
Yen
ButtonState
- Pressed
The button was activated this frame
- Held
The button is active but was not activated this frame
- Released
The button was released this frame
- NotPressed
The button is not active but was not released this frame
Fork me on GitHubqs
module
qs
module is the namespace for quicksilver library functions:
-
draw(shape, sprite and sprite animation)
-
play sound
-
initialize asset
-
get mouse position, keyboard state, etc..
The name
qs
comes from the underlying game frameworkquicksilver
.
pyckitup Types
The types in pyckitup are simple Python objects.
-
Point: A cartesian point represented as a list
[x, y]
. -
Color: Linear RGBA type. It is a list of four floats with range [0., 1.]. Example:
BLUE = [0, 0, 1, 1]
-
Transform: 3x3 Transformation matrix. a list of lists that represents a 3x3 matrix with the last being homogeneous coordinate.
def rotate(deg):
theta = deg * 3.14 / 180.
c = math.cos(theta)
s = math.sin(theta)
return [
[c,-s, 0],
[s, c, 0],
[0, 0, 1]
]
def scale(x, y):
return [
[x, 0, 0],
[0, y, 0],
[0, 0, 1]
]
def matmul(X, Y):
return [[sum(a*b for a,b in zip(X_row,Y_col)) for Y_col in zip(*Y)] for X_row in X]
Draw
All draw calls have these common optional named arguments:
qs.rect(.., color: Color, transform: Transform, z: int)
These are the default values for the optional arguments:
Color: Red Transform: Identity z: 0
Shapes
Rectangle
qs.rect([Point, [w, h]], ..)
Example:
# Draw a blue rectangle with a top-left corner at
# (100, 100) and a width and height of 32
qs.rect([[100,100], [32,32]], color=BLUE)
Circle
qs.circ([Point], radius: float, ..) # radius is required
Example:
# Draw a green circle with its center at (400, 300) and a radius of 100
qs.circ( [400, 300], 100., color=GREEN)
Triangle
qs.triangle([Point, Point, Point], ..)
Example:
# Draw a red triangle rotated by 45 degrees, and scaled down to half
qs.triangle([[500, 50], [450, 100], [650, 150]],
color=RED, transform=matmul(rotate(45), scale(0.5, 0.5)), z=0)
Line
qs.line([Point, Point], thickness: int, ..) # thickness is an optional named argument with default=1
Example:
# Draw a red line with thickness of 2 pixels and z-height of 5
qs.line([[50, 80], [600, 450]], thickness=2., color=RED, z=5)
Assets
Sprites are uniquely identified by name and must be initialized in the init
lifecycle function.
Image drawing functions can be called in two ways:
.., rect=[Point, Point], ..
Use this to draw your sprite in the specified bounding rectangle.
.., p0=Point, ..
Align the top left corner of the sprite with p0 and leave width and height unchanged.
Sprite
qs.sprite(sprite_name: str, rect=[Point, Point], ..)
# or
qs.sprite(sprite_name: str, p0=Point, ..)
Animation
qs.anim(animation_name: str, rect=[Point, Point], ..)
# or
qs.anim(animation_name: str, p0=Point, ..)
Sound
Sound is also unique identified by name. This function plays the sound:
qs.sound(name: str)
Initializers
Sprites
args: [(name, path)]
Sprites Animations
args: [(name, path, number of frames, duration(in s))]
Sounds
args: [(name, path)]
Example:
qs.init_sprites([
["crab", "crab.png"], # name, path
])
qs.init_anims([
["crab-left", "crab-left.png", 2, 1.], # number of frames, duration(in s)
])
qs.init_sounds([
["click", "click.wav"]
])
misc
qs.mouse_pos()
qs.keyboard()
qs.keyboard_bool()
qs.mouse_wheel_delta()
qs.set_view()
qs.set_update_rate()
qs.update_rate()
qs.clear()
Fork me on GitHub
Contributing
pyckitup mainly uses RustPython and quicksilver to do all the heavy lifting. Most of pyckitup is glue between these two crates.
Quicksilver supports both native and web backends so the idea is to include a python interpreter and glue the game loop to Python functions.
cargo install cargo-web --git https://github.com/rickyhan/cargo-web --force
# git clone https://github.com/rickyhan/RustPython
git clone git@github.com:pickitup247/pyckitup.git
cd pyckitup
cargo web deploy --release
cargo build --release
Contact
Please email me at pickitup247@rickyhan.com