This Python library converts Python code into JSON format and sends it to the Voxelamming app using WebSockets, allowing users to create 3D voxel models by writing Python scripts.
Voxelamming is an AR programming learning app. Even programming beginners can learn programming visually and enjoyably. Voxelamming supports iPhones and iPads with iOS 16 or later, and Apple Vision Pro.
pip install voxelamming
from voxelamming import Voxelamming
# Set your room name. This should match the room name displayed in your Voxelamming app.
room_name = "1000"
# Create a Voxelamming instance
voxelamming = Voxelamming(room_name)
# Define Python code to create a voxel model
voxelamming.set_box_size(0.5)
voxelamming.set_build_interval(0.01)
voxelamming.transform(0, 0, 0, pitch=0, yaw=0, roll=0)
voxelamming.animate(0, 0, 10, pitch=0, yaw=30, roll=0, scale=2, interval= 10)
for i in range(100):
voxelamming.create_box(-1, i, 0, r=0, g=1, b=1)
voxelamming.create_box(0, i, 0, r=1, g=0, b=0)
voxelamming.create_box(1, i, 0, r=1, g=1, b=0)
voxelamming.create_box(2, i, 0, r=0, g=1, b=1)
for i in range(50):
voxelamming.remove_box(0, i * 2 + 1, 0)
voxelamming.remove_box(1, i * 2, 0)
# Send the Python code to the Voxelamming app
voxelamming.send_data()
import pyxel
import time
import random
# from voxelamming import Voxelamming
from voxelamming_local import Voxelamming # Use this when developing locally
class Player:
name = 'spaceship_8x8'
dot_data = (
'-1 -1 -1 8 8 -1 -1 -1 -1 -1 3 7 7 3 -1 -1 -1 -1 -1 7 7 -1 -1 -1 -1 -1 7 7 7 7 -1 -1 -1 7 7 7 7 7 7 -1 3 7'
' 7 7 7 7 7 3 -1 8 8 7 7 8 8 -1 -1 -1 -1 8 8 -1 -1 -1'
)
def __init__(self, x, y, speed):
self.direction = 0
self.x = x
self.y = y
self.img = 0
self.u = 0
self.v = 0
self.w = 8
self.h = 8
self.speed = speed
def update(self):
if pyxel.btn(pyxel.KEY_LEFT):
self.x -= self.speed
if pyxel.btn(pyxel.KEY_RIGHT):
self.x += self.speed
class Enemy:
name = 'enemy_8x8'
dot_data = (
'-1 -1 3 -1 -1 3 -1 -1 -1 3 -1 3 3 -1 3 -1 3 -1 3 3 3 3 -1 3 3 3 3 3 3 3 3 3 3 3 -1 3 3 -1 3 3 3 3 3 3 3 3'
' 3 3 -1 3 3 -1 -1 3 3 -1 3 -1 -1 -1 -1 -1 -1 3'
)
def __init__(self, x, y):
self.direction = 0
self.x = x
self.y = y
self.img = 0
self.u = 0
self.v = 8
self.w = 8
self.h = 8
class Missile:
def __init__(self, x, y, color_id, direction=0, width=1, height=1):
self.x = x
self.y = y
self.direction = direction
self.color_id = color_id
self.width = width
self.height = height
class App:
def __init__(self):
# Pyxel settings
self.window_width = 160 # The width of the AR window becomes the value multiplied by self.dot_size (in centimeters)
self.window_height = 120 # The height of the AR window becomes the value multiplied by self.dot_size (in centimeters)
self.score = 0
self.game_over = False
self.game_clear = False
# Player settings
self.player = Player(self.window_width // 2, self.window_height - 10, 2)
self.missiles = []
self.player_missile_speed = 2
# Enemy settings
self.enemy_rows = 3
self.enemy_cols = 6
self.enemy_speed = 1
self.enemy_direction = 1
self.enemies = []
self.enemy_missiles = []
self.enemy_missile_speed = 2
# Initialize enemies
for row in range(self.enemy_rows):
for col in range(self.enemy_cols):
enemy_x = col * 16 + 20
enemy_y = row * 12 + 20
enemy = Enemy(enemy_x, enemy_y)
self.enemies.append(enemy)
# Voxelamming settings (executed before Pyxel initialization)
self.dot_size = 1 # The size of the sprite dots displayed in the AR space (in centimeters)
self.window_angle = 80 # Tilt angle of the AR window (in degrees)
self.vox = Voxelamming('1000')
self.init_voxelamming()
# Pyxel initialization
pyxel.init(self.window_width, self.window_height, title="Pyxel Invader Game", fps=30)
pyxel.mouse(True)
pyxel.load("invader_game.pyxres")
pyxel.run(self.update, self.draw)
def update(self):
if self.game_over or self.game_clear:
# Show cursor
pyxel.mouse(True)
if pyxel.btnp(pyxel.MOUSE_BUTTON_LEFT):
self.reset_game()
return
# Hide cursor
pyxel.mouse(False)
# Player controls
self.player.update()
if pyxel.btnp(pyxel.KEY_SPACE):
missile_x = self.player.x + self.player_missile_speed
missile_y = self.player.y
missile_clor_id = 10 # Blue
missile_direction = 0
missile_width = 2
missile_height = 4
self.missiles.append(
Missile(missile_x, missile_y, missile_clor_id, missile_direction, missile_width, missile_height))
# Move missiles
for missile in self.missiles[:]:
missile.y -= 2
if missile.y < 0:
self.missiles.remove(missile)
# Move enemies
move_down = False
for enemy in self.enemies:
enemy.x += self.enemy_speed * self.enemy_direction
for enemy in self.enemies:
if enemy.x > pyxel.width - 8 or enemy.x < 0:
self.enemy_direction *= -1
move_down = True
break # Change direction immediately when reaching the edge
if move_down:
for enemy in self.enemies:
enemy.y += 8
# Game over if the enemy reaches the bottom of the screen
if enemy.y > pyxel.height - 16:
self.game_over = True
# Enemy missile firing
if random.random() < 0.03 and self.enemies:
shooting_enemy = random.choice(self.enemies)
missile_x = shooting_enemy.x + 4
missile_y = shooting_enemy.y + 8
missile_clor_id = 8 # Red
missile_direction = 0
missile_width = 2
missile_height = 4
self.enemy_missiles.append(
Missile(missile_x, missile_y, missile_clor_id, missile_direction, missile_width, missile_height))
# Move enemy missiles
for missile in self.enemy_missiles[:]:
missile.y += self.enemy_missile_speed
if missile.y > pyxel.height * 2:
self.enemy_missiles.remove(missile)
# Collision detection between missiles and enemies
for missile in self.missiles[:]:
for enemy in self.enemies[:]:
if (enemy.x < missile.x < enemy.x + 16 and
enemy.y < missile.y < enemy.y + 12):
self.missiles.remove(missile)
self.enemies.remove(enemy)
self.score += 10
break
# Collision detection between player and enemy missiles
for missile in self.enemy_missiles[:]:
if (self.player.x < missile.x < self.player.x + 8 and
self.player.y < missile.y < self.player.y + 8):
self.game_over = True
# Collision detection between player and enemies
for enemy in self.enemies:
if (self.player.x < enemy.x < self.player.x + 8 and
self.player.y < enemy.y < self.player.y + 8):
self.game_over = True
# Check for game clear
if not self.enemies:
self.game_clear = True
# Update Voxelamming
self.update_voxelamming()
def draw(self):
pyxel.cls(0)
pyxel.text(5, 4, f"Score: {self.score}", 7)
if self.game_clear:
pyxel.text(pyxel.width // 2 - 20, pyxel.height // 2, "GAME CLEAR!", pyxel.frame_count % 16)
pyxel.text(self.window_width // 2 - 26, self.window_height // 2 + 8, "Click to start",
pyxel.frame_count % 16)
elif self.game_over:
pyxel.text(pyxel.width // 2 - 20, pyxel.height // 2, "GAME OVER", pyxel.frame_count % 16)
pyxel.text(self.window_width // 2 - 26, self.window_height // 2 + 8, "Click to start",
pyxel.frame_count % 16)
else:
# Draw player
pyxel.blt(self.player.x, self.player.y, self.player.img, self.player.u, self.player.v, self.player.w,
self.player.h, 0)
# Draw enemies
for enemy in self.enemies:
pyxel.blt(enemy.x, enemy.y, enemy.img, enemy.u, enemy.v, enemy.w, enemy.h, 0)
# Draw missiles
for missile in self.missiles:
pyxel.rect(missile.x, missile.y, missile.width, missile.height, missile.color_id)
# Draw enemy missiles
for missile in self.enemy_missiles:
pyxel.rect(missile.x, missile.y, missile.width, missile.height, missile.color_id)
def reset_game(self):
self.score = 0 # Reset score
self.game_over = False
self.game_clear = False
# Player settings
self.player = Player(self.window_width // 2, self.window_height - 10, 2)
self.missiles = []
# Enemy settings
self.enemy_rows = 3
self.enemy_cols = 6
self.enemy_speed = 1
self.enemy_direction = 1
self.enemies = []
self.enemy_missiles = []
self.enemy_missile_speed = 2
# Initialize enemies
for row in range(self.enemy_rows):
for col in range(self.enemy_cols):
enemy_x = col * 16 + 20
enemy_y = row * 12 + 20
enemy = Enemy(enemy_x, enemy_y)
self.enemies.append(enemy)
def init_voxelamming(self):
# Initialize Voxelamming
self.vox.set_box_size(self.dot_size)
self.vox.set_game_screen(self.window_width, self.window_height, self.window_angle, red=1, green=1, blue=0,
alpha=0.8)
self.vox.set_game_score(self.score)
# Display the player's sprite
vox_x, vox_y = self.convert_sprite_position_to_voxelamming(self.player.x, self.player.y)
self.vox.create_sprite(self.player.name, self.player.dot_data, vox_x, vox_y, self.player.direction, 1)
# Since there are multiple enemies, create a template and display it in multiple locations
self.vox.create_sprite(Enemy.name, Enemy.dot_data)
for enemy in self.enemies:
vox_x, vox_y = self.convert_sprite_position_to_voxelamming(enemy.x, enemy.y)
self.vox.move_sprite(enemy.name, vox_x, vox_y, enemy.direction, 1)
self.vox.send_data()
self.vox.clear_data()
def update_voxelamming(self):
# Send sprite information every 0.1 seconds
if pyxel.frame_count % 3 == 0 or self.game_clear or self.game_over: # Default Pyxel FPS is 30
self.vox.set_box_size(self.dot_size)
self.vox.set_game_screen(self.window_width, self.window_height, self.window_angle, red=1, green=1,
blue=0, alpha=0.5)
self.vox.set_game_score(self.score, -66, 57)
# Move sprites
vox_x, vox_y = self.convert_sprite_position_to_voxelamming(self.player.x, self.player.y)
self.vox.move_sprite(self.player.name, vox_x, vox_y, self.player.direction, 1)
# Enemy movement is displayed as templates in multiple locations
for enemy in self.enemies:
vox_x, vox_y = self.convert_sprite_position_to_voxelamming(enemy.x, enemy.y)
self.vox.move_sprite_clone(enemy.name, vox_x, vox_y, enemy.direction, 1)
# Missiles are displayed as dots
for missile in self.missiles + self.enemy_missiles:
vox_x, vox_y = self.convert_dot_position_to_voxelamming(missile.x, missile.y, missile.width, missile.height)
self.vox.display_dot(vox_x, vox_y, missile.direction, missile.color_id, missile.width,
missile.height)
# Change the screen to blue and display the game clear
if self.game_clear:
self.vox.set_game_screen(self.window_width, self.window_height, self.window_angle, red=0, green=0,
blue=1, alpha=0.8)
self.vox.set_command('gameClear')
# Change the screen to red and display game over
if self.game_over:
self.vox.set_game_screen(self.window_width, self.window_height, self.window_angle, red=1, green=0,
blue=0, alpha=0.8)
self.vox.set_command('gameOver')
self.vox.send_data()
# Wait for 1 second after game clear or game over, then send data again
if self.game_clear or self.game_over:
time.sleep(1)
self.vox.send_data()
self.vox.clear_data()
def convert_sprite_position_to_voxelamming(self, x, y):
return x - self.window_width // 2 + 4, self.window_height // 2 - (y + 4)
def convert_dot_position_to_voxelamming(self, x, y, width=1, height=1):
return x - self.window_width // 2 + width / 2, self.window_height // 2 - (y + height / 2)
if __name__ == "__main__":
App()
This code snippet demonstrates a simple example where a red voxel is created at a specific location. You can use various functions provided by the Voxelamming
class to build more complex models.
Modeling Method name | Description | Arguments |
---|---|---|
set_room_name(room_name) | Sets the room name for communicating with the device. | room_name : Room name (string) |
set_box_size(size) | Sets the size of the voxel (default: 1.0). | size : Size (float) |
set_build_interval(interval) | Sets the placement interval of the voxels (default: 0.01 seconds). | interval : Interval (float) |
change_shape(shape) | Changes the shape of the voxel. | shape : Shape ("box", "sphere", "plane") |
change_material(is_metallic, roughness) | Changes the material of the voxel. | is_metallic : Whether to make it metallic (boolean), roughness : Roughness (float) |
create_box(x, y, z, r, g, b, alpha) | Places a voxel. | x , y , z : Position (float), r , g , b , alpha : Color (float, 0-1) |
create_box(x, y, z, texture) | Places a voxel with texture. | x , y , z : Position (float), texture : Texture name (string) |
remove_box(x, y, z) | Removes a voxel. | x , y , z : Position (float) |
write_sentence(sentence, x, y, z, r, g, b, alpha) | Draws a string with voxels. | sentence : String (string), x , y , z : Position (float), r , g , b , alpha : Color (float, 0-1) |
set_light(x, y, z, r, g, b, alpha, intensity, interval, light_type) | Places a light. | x , y , z : Position (float), r , g , b , alpha : Color (float, 0-1), intensity : Intensity (float), interval : Blinking interval (float), light_type : Type of light ("point", "spot", "directional") |
set_command(command) | Executes a command. | command : Command ("axis", "japaneseCastle", "float", "liteRender") |
draw_line(x1, y1, z1, x2, y2, z2, r, g, b, alpha) | Draws a line between two points. | x1 , y1 , z1 : Starting point (float), x2 , y2 , z2 : Ending point (float), r , g , b , alpha : Color (float, 0-1) |
create_model(model_name, x, y, z, pitch, yaw, roll, scale, entity_name) | Creates a built-in model (USDZ). | model_name : Name of the model (string), x , y , z : Translation values (float), pitch , yaw , roll : Rotation values (float), scale : Scale (float), entity_name : Name assigned to the created model (string) |
move_model(entity_name, x, y, z, pitch, yaw, roll, scale) | Moves the created model (USDZ). | entity_name : Name assigned to the created model (string), x , y , z : Translation values (float), pitch , yaw , roll : Rotation values (float), scale : Scale (float) |
send_data(name) | Sends voxel data to the device; if the name argument is set, the voxel data can be stored and reproduced as history. | |
clear_data() | Initializes voxel data. | |
transform(x, y, z, pitch, yaw, roll) | Moves and rotates the coordinate system of the voxel. | x , y , z : Translation amount (float), pitch , yaw , roll : Rotation amount (float) |
animate(x, y, z, pitch, yaw, roll, scale, interval) | Animates a voxel. | x , y , z : Translation amount (float), pitch , yaw , roll : Rotation amount (float), scale : Scale (float), interval : Interval (float) |
animate_global(x, y, z, pitch, yaw, roll, scale, interval) | Animates all voxels. | x , y , z : Translation amount (float), pitch , yaw , roll : Rotation amount (float), scale : Scale (float), interval : Interval (float) |
push_matrix() | Saves the current coordinate system to the stack. | |
pop_matrix() | Restores the coordinate system from the stack. | |
frame_in() | Starts recording a frame. | |
frame_out() | Ends recording a frame. | |
set_frame_fps(fps) | Sets the frame rate (default: 2). | fps : Frame rate (int) |
set_frame_repeats(repeats) | Sets the number of frame repetitions (default: 10). | repeats : Number of repetitions (int) |
Game Method Name | Description | Arguments |
set_game_screen_size(width, height, angle=90, r=1, g=1, b=0, alpha=0.5) | Sets the game screen size. | width , height : screen size (float), angle : angle (float), r , g , b , alpha : color (float, 0-1) |
set_game_score(score) | Sets the game score. | score : game score (int) |
send_game_over() | Triggers game over. | |
send_game_clear() | Triggers game clear. | |
create_sprite(sprite_name, color_list, x, y, direction=90, scale=1, visible=True) | Creates a sprite. | sprite_name : sprite name (string), color_list : dot color data (string), x , y : position (float), direction : angle (float), scale : scale (float), visible : visibility (boolean) |
move_sprite(sprite_name, x, y, direction=90, scale=1, visible=True) | Moves a sprite. | sprite_name : sprite name (string), x , y : position (float), direction : angle (float), scale : scale (float), visible : visibility (boolean) |
move_sprite_clone(sprite_name, x, y, direction=90, scale=1,) | Moves a clone of the sprite. Can be executed multiple times and is used when creating multiple sprites. | sprite_name : Sprite name (string), x , y : Position (float), direction : Direction (float), scale : Scale (float) |
display_dot(sprite_name, x, y, direction=90, scale=1) | Used to place multiple dots, such as bullets or particles. | sprite_name : Sprite name (string), x , y : Position (float), direction : Direction (float), scale : Scale (float) |
display_text(sprite_name, x, y, direction=90, scale=1, is_vertical=True) | Displays text on the game screen. | sprite_name : Sprite name (string), x , y : Position (float), direction : Direction (float), scale : Scale (float), is_vertical : Vertical display (boolean) |
This library is under active development. More features and improvements are planned for future releases.
creativival