Skip to content

Commit 280958a

Browse files
Add new app: Confetti
1 parent bae7fb0 commit 280958a

File tree

6 files changed

+140
-0
lines changed

6 files changed

+140
-0
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "Confetti",
3+
"publisher": "MicroPythonOS",
4+
"short_description": "Just shows confetti",
5+
"long_description": "Nothing special, just a demo.",
6+
"icon_url": "https://apps.micropythonos.com/apps/com.micropythonos.confetti/icons/com.micropythonos.confetti_0.0.1_64x64.png",
7+
"download_url": "https://apps.micropythonos.com/apps/com.micropythonos.confetti/mpks/com.micropythonos.confetti_0.0.1.mpk",
8+
"fullname": "com.micropythonos.confetti",
9+
"version": "0.0.1",
10+
"category": "games",
11+
"activities": [
12+
{
13+
"entrypoint": "assets/confetti.py",
14+
"classname": "Confetti",
15+
"intent_filters": [
16+
{
17+
"action": "main",
18+
"category": "launcher"
19+
}
20+
]
21+
}
22+
]
23+
}
24+
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import time
2+
import random
3+
import lvgl as lv
4+
5+
from mpos.apps import Activity, Intent
6+
import mpos.config
7+
import mpos.ui
8+
9+
class Confetti(Activity):
10+
# === CONFIG ===
11+
SCREEN_WIDTH = 320
12+
SCREEN_HEIGHT = 240
13+
ASSET_PATH = "M:apps/com.micropythonos.confetti/res/drawable-mdpi/"
14+
MAX_CONFETTI = 21
15+
GRAVITY = 100 # pixels/sec²
16+
17+
def onCreate(self):
18+
print("Confetti Activity starting...")
19+
20+
# Background
21+
self.screen = lv.obj()
22+
self.screen.set_style_bg_color(lv.color_hex(0x000033), 0) # Dark blue
23+
self.screen.set_scrollbar_mode(lv.SCROLLBAR_MODE.OFF)
24+
self.screen.remove_flag(lv.obj.FLAG.SCROLLABLE)
25+
26+
# Timing
27+
self.last_time = time.ticks_ms()
28+
29+
# Confetti state
30+
self.confetti_pieces = []
31+
self.confetti_images = []
32+
self.used_img_indices = set() # Track which image slots are in use
33+
34+
# Pre-create LVGL image objects
35+
for i in range(self.MAX_CONFETTI):
36+
img = lv.image(self.screen)
37+
img.set_src(f"{self.ASSET_PATH}confetti{random.randint(1,3)}.png")
38+
img.add_flag(lv.obj.FLAG.HIDDEN)
39+
self.confetti_images.append(img)
40+
41+
# Spawn initial confetti
42+
for _ in range(self.MAX_CONFETTI):
43+
self.spawn_confetti()
44+
45+
self.setContentView(self.screen)
46+
47+
def onResume(self, screen):
48+
mpos.ui.th.add_event_cb(self.update_frame, 1)
49+
50+
def onPause(self, screen):
51+
mpos.ui.th.remove_event_cb(self.update_frame)
52+
53+
def spawn_confetti(self):
54+
"""Safely spawn a new confetti piece with unique img_idx"""
55+
# Find a free image slot
56+
for idx, img in enumerate(self.confetti_images):
57+
if img.has_flag(lv.obj.FLAG.HIDDEN) and idx not in self.used_img_indices:
58+
break
59+
else:
60+
return # No free slot
61+
62+
piece = {
63+
'img_idx': idx,
64+
'x': random.uniform(-10, self.SCREEN_WIDTH + 10),
65+
'y': random.uniform(50, 100),
66+
'vx': random.uniform(-100, 100),
67+
'vy': random.uniform(-250, -80),
68+
'spin': random.uniform(-400, 400),
69+
'age': 0.0,
70+
'lifetime': random.uniform(1.8, 5),
71+
'rotation': random.uniform(0, 360),
72+
'scale': 1.0
73+
}
74+
self.confetti_pieces.append(piece)
75+
self.used_img_indices.add(idx)
76+
77+
def update_frame(self, a, b):
78+
current_time = time.ticks_ms()
79+
delta_ms = time.ticks_diff(current_time, self.last_time)
80+
delta_time = delta_ms / 1000.0
81+
self.last_time = current_time
82+
83+
new_pieces = []
84+
85+
for piece in self.confetti_pieces:
86+
# === UPDATE PHYSICS ===
87+
piece['age'] += delta_time
88+
piece['x'] += piece['vx'] * delta_time
89+
piece['y'] += piece['vy'] * delta_time
90+
piece['vy'] += self.GRAVITY * delta_time
91+
piece['rotation'] += piece['spin'] * delta_time
92+
piece['scale'] = max(0.3, 1.0 - (piece['age'] / piece['lifetime']) * 0.7)
93+
94+
# === UPDATE LVGL IMAGE ===
95+
img = self.confetti_images[piece['img_idx']]
96+
img.remove_flag(lv.obj.FLAG.HIDDEN)
97+
img.set_pos(int(piece['x']), int(piece['y']))
98+
img.set_rotation(int(piece['rotation'] * 10)) # LVGL: 0.1 degrees
99+
img.set_scale(int(256 * piece['scale']* 2)) # 256 = 100%
100+
101+
# === CHECK IF DEAD ===
102+
off_screen = (
103+
piece['x'] < -60 or piece['x'] > self.SCREEN_WIDTH + 60 or
104+
piece['y'] > self.SCREEN_HEIGHT + 60
105+
)
106+
too_old = piece['age'] > piece['lifetime']
107+
108+
if off_screen or too_old:
109+
img.add_flag(lv.obj.FLAG.HIDDEN)
110+
self.used_img_indices.discard(piece['img_idx'])
111+
self.spawn_confetti() # Replace immediately
112+
else:
113+
new_pieces.append(piece)
114+
115+
# === APPLY NEW LIST ===
116+
self.confetti_pieces = new_pieces
163 Bytes
Loading
415 Bytes
Loading
547 Bytes
Loading
2.78 KB
Loading

0 commit comments

Comments
 (0)