mirror of
https://github.com/Yonokid/PyTaiko.git
synced 2026-02-04 11:40:13 +01:00
many optimizations and bug fixes
This commit is contained in:
@@ -44,7 +44,7 @@ ffi.cdef("""
|
||||
|
||||
// Device management
|
||||
void list_host_apis(void);
|
||||
void init_audio_device(PaHostApiIndex host_api, double sample_rate);
|
||||
void init_audio_device(PaHostApiIndex host_api, double sample_rate, unsigned long buffer_size);
|
||||
void close_audio_device(void);
|
||||
bool is_audio_device_ready(void);
|
||||
void set_master_volume(float volume);
|
||||
@@ -128,11 +128,12 @@ except OSError as e:
|
||||
raise
|
||||
|
||||
class AudioEngine:
|
||||
def __init__(self, device_type: int, sample_rate: float):
|
||||
def __init__(self, device_type: int, sample_rate: float, buffer_size: int):
|
||||
self.device_type = device_type
|
||||
if sample_rate == -1:
|
||||
sample_rate = 44100
|
||||
self.target_sample_rate = sample_rate
|
||||
self.buffer_size = buffer_size
|
||||
self.sounds = {} # sound_id -> sound struct
|
||||
self.music_streams = {} # music_id -> music struct
|
||||
self.sound_counter = 0
|
||||
@@ -145,7 +146,7 @@ class AudioEngine:
|
||||
def init_audio_device(self) -> bool:
|
||||
"""Initialize the audio device"""
|
||||
try:
|
||||
lib.init_audio_device(self.device_type, self.target_sample_rate) # type: ignore
|
||||
lib.init_audio_device(self.device_type, self.target_sample_rate, self.buffer_size) # type: ignore
|
||||
self.audio_device_ready = lib.is_audio_device_ready() # type: ignore
|
||||
if self.audio_device_ready:
|
||||
print("Audio device initialized successfully")
|
||||
@@ -361,5 +362,5 @@ class AudioEngine:
|
||||
return 0.0
|
||||
|
||||
# Create the global audio instance
|
||||
audio = AudioEngine(get_config()["audio"]["device_type"], get_config()["audio"]["sample_rate"])
|
||||
audio = AudioEngine(get_config()["audio"]["device_type"], get_config()["audio"]["sample_rate"], get_config()["audio"]["buffer_size"])
|
||||
audio.set_master_volume(0.75)
|
||||
|
||||
@@ -114,7 +114,7 @@ typedef struct AudioData {
|
||||
// Function declarations
|
||||
// Device management
|
||||
void list_host_apis(void);
|
||||
void init_audio_device(PaHostApiIndex host_api, double sample_rate);
|
||||
void init_audio_device(PaHostApiIndex host_api, double sample_rate, unsigned long buffer_size);
|
||||
void close_audio_device(void);
|
||||
bool is_audio_device_ready(void);
|
||||
void set_master_volume(float volume);
|
||||
@@ -322,7 +322,7 @@ PaDeviceIndex get_best_output_device_for_host_api(PaHostApiIndex hostApi)
|
||||
}
|
||||
|
||||
// Device management implementations
|
||||
void init_audio_device(PaHostApiIndex host_api, double sample_rate)
|
||||
void init_audio_device(PaHostApiIndex host_api, double sample_rate, unsigned long buffer_size)
|
||||
{
|
||||
// Initialize PortAudio
|
||||
PaError err = Pa_Initialize();
|
||||
@@ -358,7 +358,7 @@ void init_audio_device(PaHostApiIndex host_api, double sample_rate)
|
||||
NULL, // No input
|
||||
&AUDIO.System.outputParameters, // Output parameters
|
||||
sample_rate, // Sample rate
|
||||
paFramesPerBufferUnspecified, // Frames per buffer (let PortAudio decide)
|
||||
buffer_size, // Frames per buffer (let PortAudio decide)
|
||||
paClipOff, // No clipping
|
||||
port_audio_callback, // Callback function
|
||||
NULL); // User data
|
||||
|
||||
@@ -79,7 +79,7 @@ void list_host_apis(void);
|
||||
* Initialize the audio device and system
|
||||
* Must be called before using any other audio functions
|
||||
*/
|
||||
void init_audio_device(PaHostApiIndex host_api, double sample_rate);
|
||||
void init_audio_device(PaHostApiIndex host_api, double sample_rate, unsigned long buffer_size);
|
||||
|
||||
/**
|
||||
* Close the audio device and cleanup resources
|
||||
|
||||
@@ -51,13 +51,26 @@ class BGFever1(BGFeverBase):
|
||||
self.overlay_move_down.delay_saved = 300
|
||||
|
||||
self.wave_spin = tex.get_animation(28)
|
||||
self.wave_origin = ray.Vector2(tex.textures[self.name]['wave'].width/2,tex.textures[self.name]['wave'].height/2)
|
||||
self.circle = {
|
||||
"center_x": 100,
|
||||
"center_y": 130,
|
||||
"radius": 200,
|
||||
}
|
||||
self.lookup_table_size = 360 # One entry per degree
|
||||
self._precomputed_positions: list[tuple[float, float]] = []
|
||||
for i in range(self.lookup_table_size):
|
||||
angle = (i / self.lookup_table_size) * 2 * math.pi
|
||||
x = self.circle["center_x"] + math.cos(angle) * self.circle["radius"]
|
||||
y = self.circle["center_y"] + math.sin(angle) * self.circle["radius"]
|
||||
self._precomputed_positions.append((x, y))
|
||||
self.bg_move = tex.get_animation(16)
|
||||
|
||||
def _get_wave_position(self):
|
||||
normalized = (self.wave_spin.attribute % 180) / 180.0
|
||||
index = int(normalized * (self.lookup_table_size - 1))
|
||||
return self._precomputed_positions[index]
|
||||
|
||||
def start(self):
|
||||
self.corner_move_up.start()
|
||||
self.corner_move_down.start()
|
||||
@@ -94,11 +107,8 @@ class BGFever1(BGFeverBase):
|
||||
for i, tile in enumerate(self.bg_tiles):
|
||||
tile.draw(tex, self.name, (i*128)-self.bg_move.attribute, i % 10)
|
||||
tex.draw_texture(self.name, 'mountain', y=-self.mountain_move_up.attribute+self.mountain_move_down.attribute)
|
||||
angle = math.radians(self.wave_spin.attribute*2)
|
||||
wave_x = self.circle["center_x"] + math.cos(angle) * self.circle["radius"]
|
||||
wave_y = self.circle["center_y"] + math.sin(angle) * self.circle["radius"]
|
||||
wave_origin = ray.Vector2(tex.textures[self.name]['wave'].width/2,tex.textures[self.name]['wave'].height/2)
|
||||
tex.draw_texture(self.name, 'wave', x=wave_x, y=wave_y, origin=wave_origin)
|
||||
wave_x, wave_y = self._get_wave_position()
|
||||
tex.draw_texture(self.name, 'wave', x=wave_x, y=wave_y, origin=self.wave_origin)
|
||||
tex.draw_texture(self.name, 'footer', y=-self.footer_move_up.attribute+self.footer_move_down.attribute)
|
||||
tex.draw_texture(self.name, 'corner', y=-self.corner_move_up.attribute+self.corner_move_down.attribute)
|
||||
tex.draw_texture(self.name, 'overlay', y=self.overlay_move_up.attribute-self.overlay_move_down.attribute)
|
||||
@@ -112,6 +122,7 @@ class BGFever2(BGFeverBase):
|
||||
self.ship_rotation.start()
|
||||
self.move_in = tex.get_animation(22)
|
||||
self.move_out = tex.get_animation(23)
|
||||
self.ship_origin = ray.Vector2(tex.textures[self.name]['ship'].width/2, tex.textures[self.name]['ship'].height/2)
|
||||
|
||||
def start(self):
|
||||
self.fadein.start()
|
||||
@@ -133,8 +144,7 @@ class BGFever2(BGFeverBase):
|
||||
tex.draw_texture(self.name, 'footer_2', y=self.move_in.attribute-self.move_out.attribute, fade=self.fadein.attribute)
|
||||
tex.draw_texture(self.name, 'bird', index=0, x=self.move_in.attribute-self.move_out.attribute, mirror='horizontal', y=self.ship_rotation.attribute*180)
|
||||
tex.draw_texture(self.name, 'bird', index=1, x=-self.move_in.attribute+self.move_out.attribute, y=self.ship_rotation.attribute*180)
|
||||
origin = ray.Vector2(tex.textures[self.name]['ship'].width/2, tex.textures[self.name]['ship'].height/2)
|
||||
tex.draw_texture(self.name, 'ship', x=origin.x, y=origin.y + self.move_in.attribute-self.move_out.attribute, origin=origin, rotation=self.ship_rotation.attribute*100, center=True)
|
||||
tex.draw_texture(self.name, 'ship', x=self.ship_origin.x, y=self.ship_origin.y + self.move_in.attribute-self.move_out.attribute, origin=self.ship_origin, rotation=self.ship_rotation.attribute*100, center=True)
|
||||
|
||||
class BGFever3(BGFeverBase):
|
||||
def __init__(self, tex: TextureWrapper, index: int, path: str):
|
||||
@@ -152,10 +162,53 @@ class BGFever3(BGFeverBase):
|
||||
"center_y": 300,
|
||||
"radius": 300,
|
||||
}
|
||||
|
||||
self.num_fish = 8
|
||||
self.fish_spacing = (2 * math.pi) / self.num_fish # 45 degrees in radians
|
||||
|
||||
self.wave_origin = ray.Vector2(tex.textures[self.name]['wave'].width/2, tex.textures[self.name]['wave'].height/2)
|
||||
self.fish_origin = ray.Vector2(tex.textures[self.name]['fish'].width/2, tex.textures[self.name]['fish'].height/2)
|
||||
self.circle_origin = ray.Vector2(tex.textures[self.name]['circle'].width/2,tex.textures[self.name]['circle'].height/2)
|
||||
|
||||
self.fish_phase_offsets = [i * self.fish_spacing for i in range(self.num_fish)]
|
||||
self.lookup_table_size = 360 # One entry per degree
|
||||
|
||||
self._precomputed_main_positions: list[tuple[float, float]] = []
|
||||
for i in range(self.lookup_table_size):
|
||||
angle = (i / self.lookup_table_size) * 2 * math.pi
|
||||
x = self.circle["center_x"] + math.cos(angle) * self.circle["radius"]
|
||||
y = self.circle["center_y"] + math.sin(angle) * self.circle["radius"]
|
||||
self._precomputed_main_positions.append((x, y))
|
||||
|
||||
self._precomputed_small_positions: list[tuple[float, float]] = []
|
||||
for i in range(self.lookup_table_size):
|
||||
angle = (i / self.lookup_table_size) * 2 * math.pi
|
||||
x = self.circle["center_x"] + math.cos(angle) * 20
|
||||
y = self.circle["center_y"] + math.sin(angle) * 20
|
||||
self._precomputed_small_positions.append((x, y))
|
||||
|
||||
self._precomputed_rotations: list[float] = []
|
||||
for i in range(self.lookup_table_size):
|
||||
angle = (i / self.lookup_table_size) * 2 * math.pi
|
||||
swimming_angle = angle + math.pi/2
|
||||
swimming_rotation = math.degrees(swimming_angle)
|
||||
self._precomputed_rotations.append(swimming_rotation)
|
||||
|
||||
def _get_main_circle_position(self, spin_value: float, multiplier: float = 1.0):
|
||||
normalized = ((spin_value * multiplier) % 360) / 360.0
|
||||
index = int(normalized * (self.lookup_table_size - 1))
|
||||
return self._precomputed_main_positions[index]
|
||||
|
||||
def _get_small_circle_position(self, spin_value: float, multiplier: float = 1.0):
|
||||
normalized = ((spin_value * multiplier) % 360) / 360.0
|
||||
index = int(normalized * (self.lookup_table_size - 1))
|
||||
return self._precomputed_small_positions[index]
|
||||
|
||||
def _get_fish_position_and_rotation(self, spin_value: float, phase_offset: float):
|
||||
angle_degrees = spin_value + math.degrees(phase_offset)
|
||||
normalized = (angle_degrees % 360) / 360.0
|
||||
index = int(normalized * (self.lookup_table_size - 1))
|
||||
return self._precomputed_main_positions[index], self._precomputed_rotations[index]
|
||||
|
||||
def start(self):
|
||||
self.fadein.start()
|
||||
self.move_in.start()
|
||||
@@ -174,43 +227,26 @@ class BGFever3(BGFeverBase):
|
||||
def draw(self, tex: TextureWrapper):
|
||||
tex.draw_texture(self.name, 'background', x=-self.move_in.attribute)
|
||||
tex.draw_texture(self.name, 'overlay', frame=self.overlay_tc.attribute, fade=self.fadein.attribute)
|
||||
origin = ray.Vector2(tex.textures[self.name]['circle'].width/2, tex.textures[self.name]['circle'].height/2)
|
||||
tex.draw_texture(self.name, 'circle', x=origin.x, y=origin.y, fade=self.fadein.attribute, origin=origin, rotation=self.circle_rotate.attribute)
|
||||
|
||||
angle = math.radians(self.fish_spin.attribute*2)
|
||||
wave_x = self.circle["center_x"] + math.cos(angle) * self.circle["radius"]
|
||||
wave_y = self.circle["center_y"] + math.sin(angle) * self.circle["radius"]
|
||||
wave_origin = ray.Vector2(tex.textures[self.name]['wave'].width/2,tex.textures[self.name]['wave'].height/2)
|
||||
tex.draw_texture(self.name, 'wave', x=wave_x, y=wave_y, fade=self.fadein.attribute, origin=wave_origin)
|
||||
|
||||
tex.draw_texture(self.name, 'circle', x=self.circle_origin.x, y=self.circle_origin.y, fade=self.fadein.attribute, origin=self.circle_origin, rotation=self.circle_rotate.attribute)
|
||||
wave_x, wave_y = self._get_main_circle_position(self.fish_spin.attribute, 2.0)
|
||||
tex.draw_texture(self.name, 'wave', x=wave_x, y=wave_y, fade=self.fadein.attribute, origin=self.wave_origin)
|
||||
for j in range(2):
|
||||
for i in range(self.num_fish):
|
||||
fish_phase_offset = i * self.fish_spacing
|
||||
angle = math.radians(self.fish_spin.attribute) + fish_phase_offset
|
||||
fish_x = self.circle["center_x"] + math.cos(angle) * self.circle["radius"]
|
||||
fish_y = self.circle["center_y"] + math.sin(angle) * self.circle["radius"]
|
||||
fish_pos, fish_rotation = self._get_fish_position_and_rotation(
|
||||
self.fish_spin.attribute, self.fish_phase_offsets[i])
|
||||
fish_x, fish_y = fish_pos
|
||||
|
||||
# Fish should face the direction they're swimming (tangent to circle)
|
||||
swimming_angle = angle + math.pi/2 # Perpendicular to radius
|
||||
swimming_rotation = math.degrees(swimming_angle)
|
||||
tex.draw_texture(self.name, 'fish', x=fish_x, y=fish_y, fade=self.fadein.attribute, origin=self.fish_origin, rotation=fish_rotation, index=j)
|
||||
|
||||
fish_origin = ray.Vector2(tex.textures[self.name]['fish'].width/2,tex.textures[self.name]['fish'].height/2)
|
||||
|
||||
tex.draw_texture(self.name, 'fish', x=fish_x, y=fish_y, fade=self.fadein.attribute,
|
||||
origin=fish_origin,
|
||||
rotation=swimming_rotation,
|
||||
index=j
|
||||
)
|
||||
|
||||
angle = math.radians(self.fish_spin.attribute*3)
|
||||
wave_x = self.circle["center_x"] + math.cos(angle) * 20
|
||||
wave_y = self.circle["center_y"] + math.sin(angle) * 20
|
||||
wave_origin = ray.Vector2(tex.textures[self.name]['wave'].width/2,tex.textures[self.name]['wave'].height/2)
|
||||
footer_wave_x, footer_wave_y = self._get_small_circle_position(self.fish_spin.attribute, 3.0)
|
||||
for i in range(3):
|
||||
tex.draw_texture(self.name, 'footer_2', x=wave_x + (i*600), y=wave_y, fade=self.fadein.attribute, origin=wave_origin)
|
||||
tex.draw_texture(self.name, 'footer_2',
|
||||
x=footer_wave_x + (i*600), y=footer_wave_y,
|
||||
fade=self.fadein.attribute, origin=self.wave_origin)
|
||||
|
||||
for i in range(3):
|
||||
tex.draw_texture(self.name, 'footer_1', x=i*450, y=-self.footer_move_up.attribute)
|
||||
|
||||
tex.draw_texture(self.name, 'bird', frame=self.bird_tc.attribute, index=0, x=-self.move_in.attribute)
|
||||
tex.draw_texture(self.name, 'bird', frame=self.bird_tc.attribute, index=1, x=-self.move_in.attribute)
|
||||
|
||||
|
||||
@@ -7,15 +7,14 @@ import pyray as ray
|
||||
class Renda:
|
||||
|
||||
@staticmethod
|
||||
def create(tex: TextureWrapper, index: int, path: str):
|
||||
def create(tex: TextureWrapper, index: int):
|
||||
map = [Renda0, Renda1, Renda2]
|
||||
selected_obj = map[index]
|
||||
return selected_obj(tex, index, path)
|
||||
return selected_obj(tex, index)
|
||||
|
||||
class BaseRenda:
|
||||
def __init__(self, tex: TextureWrapper, index: int, path: str):
|
||||
def __init__(self, tex: TextureWrapper, index: int):
|
||||
self.name = 'renda_' + str(index)
|
||||
tex.load_zip(path, 'renda')
|
||||
self.hori_move = Animation.create_move(1500, total_distance=1280)
|
||||
self.hori_move.start()
|
||||
|
||||
@@ -23,8 +22,8 @@ class BaseRenda:
|
||||
self.hori_move.update(current_time_ms)
|
||||
|
||||
class Renda0(BaseRenda):
|
||||
def __init__(self, tex: TextureWrapper, index: int, path: str):
|
||||
super().__init__(tex, index, path)
|
||||
def __init__(self, tex: TextureWrapper, index: int):
|
||||
super().__init__(tex, index)
|
||||
self.vert_move = Animation.create_move(1500, total_distance=800)
|
||||
self.vert_move.start()
|
||||
self.frame = random.randint(0, 5)
|
||||
@@ -39,12 +38,13 @@ class Renda0(BaseRenda):
|
||||
tex.draw_texture('renda', self.name, frame=self.frame, x=self.hori_move.attribute+self.x, y=-self.vert_move.attribute+self.y)
|
||||
|
||||
class Renda1(BaseRenda):
|
||||
def __init__(self, tex: TextureWrapper, index: int, path: str):
|
||||
super().__init__(tex, index, path)
|
||||
def __init__(self, tex: TextureWrapper, index: int):
|
||||
super().__init__(tex, index)
|
||||
self.frame = random.randint(0, 5)
|
||||
self.y = random.randint(0, 200)
|
||||
self.rotate = Animation.create_move(800, total_distance=360)
|
||||
self.rotate.start()
|
||||
self.origin = ray.Vector2(64, 64)
|
||||
|
||||
def update(self, current_time_ms: float):
|
||||
super().update(current_time_ms)
|
||||
@@ -53,12 +53,11 @@ class Renda1(BaseRenda):
|
||||
self.rotate.restart()
|
||||
|
||||
def draw(self, tex: TextureWrapper):
|
||||
origin = ray.Vector2(64, 64)
|
||||
tex.draw_texture('renda', self.name, frame=self.frame, x=self.hori_move.attribute+origin.x, y=self.y+origin.y, origin=origin, rotation=self.rotate.attribute)
|
||||
tex.draw_texture('renda', self.name, frame=self.frame, x=self.hori_move.attribute+self.origin.x, y=self.y+self.origin.y, origin=self.origin, rotation=self.rotate.attribute)
|
||||
|
||||
class Renda2(BaseRenda):
|
||||
def __init__(self, tex: TextureWrapper, index: int, path: str):
|
||||
super().__init__(tex, index, path)
|
||||
def __init__(self, tex: TextureWrapper, index: int):
|
||||
super().__init__(tex, index)
|
||||
self.vert_move = Animation.create_move(1500, total_distance=800)
|
||||
self.vert_move.start()
|
||||
self.x = random.randint(0, 500)
|
||||
@@ -74,12 +73,13 @@ class Renda2(BaseRenda):
|
||||
class RendaController:
|
||||
def __init__(self, tex: TextureWrapper, index: int, path: str ='background'):
|
||||
self.rendas = set()
|
||||
tex.load_zip(path, 'renda')
|
||||
self.tex = tex
|
||||
self.index = index
|
||||
self.path = path
|
||||
|
||||
def add_renda(self):
|
||||
self.rendas.add(Renda.create(self.tex, self.index, self.path))
|
||||
self.rendas.add(Renda.create(self.tex, self.index))
|
||||
|
||||
def update(self, current_time_ms: float):
|
||||
remove = set()
|
||||
|
||||
@@ -76,13 +76,6 @@ def build_song_hashes(output_dir=Path("cache")):
|
||||
all_tja_files: list[Path] = []
|
||||
for root_dir in tja_paths:
|
||||
root_path = Path(root_dir)
|
||||
'''
|
||||
if (root_path / '.git').exists():
|
||||
repo = Repo(root_path)
|
||||
origin = repo.remotes.origin
|
||||
origin.pull()
|
||||
print('Pulled latest from', root_path)
|
||||
'''
|
||||
all_tja_files.extend(root_path.rglob("*.tja"))
|
||||
|
||||
global_data.total_songs = len(all_tja_files)
|
||||
|
||||
@@ -153,6 +153,7 @@ class TextureWrapper:
|
||||
self.load_zip(screen_name, zip.name)
|
||||
|
||||
def control(self, tex_object: Texture, index: int = 0):
|
||||
'''debug function'''
|
||||
distance = 1
|
||||
if ray.is_key_down(ray.KeyboardKey.KEY_LEFT_SHIFT):
|
||||
distance = 10
|
||||
|
||||
@@ -39,27 +39,6 @@ def force_dedicated_gpu():
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
def load_all_textures_from_zip(zip_path: Path) -> dict[str, list[ray.Texture]]:
|
||||
result_dict = dict()
|
||||
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
|
||||
files = zip_ref.namelist()
|
||||
for file in files:
|
||||
with zip_ref.open(file) as image_file:
|
||||
with tempfile.NamedTemporaryFile(delete=False, suffix='.png') as temp_file:
|
||||
temp_file.write(image_file.read())
|
||||
temp_file_path = temp_file.name
|
||||
texture = ray.load_texture(temp_file_path)
|
||||
os.remove(temp_file_path)
|
||||
|
||||
true_filename, index = file.split('_img')
|
||||
index = int(index.split('.')[0])
|
||||
if true_filename not in result_dict:
|
||||
result_dict[true_filename] = []
|
||||
while len(result_dict[true_filename]) <= index:
|
||||
result_dict[true_filename].append(None)
|
||||
result_dict[true_filename][index] = texture
|
||||
return result_dict
|
||||
|
||||
def rounded(num: float) -> int:
|
||||
sign = 1 if (num >= 0) else -1
|
||||
num = abs(num)
|
||||
@@ -143,6 +122,8 @@ def is_r_don_pressed() -> bool:
|
||||
if ray.is_gamepad_button_pressed(0, button):
|
||||
return True
|
||||
|
||||
if not global_data.config["general"]["touch_enabled"]:
|
||||
return False
|
||||
mid_x, mid_y = (1280//2, 720)
|
||||
allowed_gestures = {ray.Gesture.GESTURE_TAP, ray.Gesture.GESTURE_DOUBLETAP}
|
||||
if ray.get_gesture_detected() in allowed_gestures and ray.is_gesture_detected(ray.get_gesture_detected()):
|
||||
@@ -165,6 +146,8 @@ def is_l_kat_pressed() -> bool:
|
||||
if ray.is_gamepad_button_pressed(0, button):
|
||||
return True
|
||||
|
||||
if not global_data.config["general"]["touch_enabled"]:
|
||||
return False
|
||||
mid_x, mid_y = (1280//2, 720)
|
||||
allowed_gestures = {ray.Gesture.GESTURE_TAP, ray.Gesture.GESTURE_DOUBLETAP}
|
||||
if ray.get_gesture_detected() in allowed_gestures and ray.is_gesture_detected(ray.get_gesture_detected()):
|
||||
@@ -187,6 +170,8 @@ def is_r_kat_pressed() -> bool:
|
||||
if ray.is_gamepad_button_pressed(0, button):
|
||||
return True
|
||||
|
||||
if not global_data.config["general"]["touch_enabled"]:
|
||||
return False
|
||||
mid_x, mid_y = (1280//2, 720)
|
||||
allowed_gestures = {ray.Gesture.GESTURE_TAP, ray.Gesture.GESTURE_DOUBLETAP}
|
||||
if ray.get_gesture_detected() in allowed_gestures and ray.is_gesture_detected(ray.get_gesture_detected()):
|
||||
|
||||
Reference in New Issue
Block a user