feat: Add multi-platform CI/CD workflow for TempoVR#20
Merged
Conversation
- Add 'Only power balls' checkbox to Settings menu (MainMenuLeft.tscn) - Replace timing-based hit detection with velocity-squared thresholds - Expert difficulty: 1.0 min for semi-hit, 3.0 for full impact - Power balls mode: requires 3.0 velocity squared for any hit - New scoring system: 10 points (semi-hit), 20 points (full impact) - Combo multiplier now only applies after 5 consecutive hits - Update note feedback to show semi/full instead of early/late Based on PowerBeatsVR Expert difficulty settings from GAME_MECHANICS.md
When a ball is hit but the velocity is too low (TOOLOW), the ball now turns black/dark before despawning. This gives clear visual feedback that the hit didn't count/score. - Added _set_ball_dark() function to note.gd - Handles both ShaderMaterial and StandardMaterial3D - Also dims emission color if present
Based on analysis of PowerBeatsVR GameManager.cs, PowerBalls require 4x the velocity squared compared to normal balls. The code divides velocity² by 4 before checking thresholds. Updated thresholds: - Normal balls: min 1.0² (~1.0 m/s), full 3.0² (~1.73 m/s) - Power balls: min 4.0² (~2.0 m/s), full 12.0² (~3.46 m/s) Power balls mode now has tiered hits (semi/full) like normal mode, but requires significantly harder punches to score.
- Create PowerBeatsVRMap.gd: Parses PowerBeatsVR JSON format levels - Direct coordinate passthrough (matches BS->PBVR converter approach) - Maps NormalBall/PowerBall to left/right based on X position - Maps BallObstacle to bomb type (type 3) - Maps WallObstacle with wall type support (crouch, full_height, placeholders) - Handles sub-beat offsets for fractional timing - Supports Beginner/Advanced/Expert difficulties - Create MapFactory.gd: Format detection and loader creation - Auto-detects Beat Saber (folder with info.dat) vs PowerBeatsVR (.json) - Creates appropriate map loader instance - Update GameVariables.gd: Add PowerBeatsVR level paths - pbvr_layouts_path for JSON files - pbvr_music_path for audio files - Update ui_song_list.gd: Discover and display PowerBeatsVR levels - Supports both formats in song list - Uses MapFactory for loading - Update Game.gd: Use MapFactory for map loading - Works with both Beat Saber and PowerBeatsVR maps - Add test_powerbeatsvr.gd: Unit tests for level loading - JSON loading and parsing - Position coordinate mapping - Action type mapping - Beat/sub-beat parsing - Difficulty enumeration - Wall type mapping - Add POWERBEATSVR_MAP.md: Format documentation - Coordinate systems (from developers: posx=-1.3 to 1.3, posy=0.5 to 1.3) - Action and wall type mappings - File structure explanation - Include Wellerman test level with music
- Add get_ball_flight_duration() to PowerBeatsVRMap.gd - Returns 2 beats for BPM < 145 (Low/Mid range) - Returns 3 beats for BPM >= 145 (High range) - Based on PowerBeatsVR GameManager.cs Expert difficulty - Add get_ball_flight_duration() to MapLoader.gd - Returns 4 beats for Beat Saber levels (original behavior) - Update Game.gd to use map's ball flight duration - Dynamically sets notes_delay from map instead of hardcoded 4 - Maintains backward compatibility with Beat Saber levels - Add unit test for ball flight duration calculation - Tests Wellerman (96 BPM) returns 2 beats correctly - Verifies BPM threshold constants - Update POWERBEATSVR_MAP.md with timing documentation Wellerman timing: 96 BPM * 2 beats = 1.25 seconds flight time (Previously 96 BPM * 4 beats = 2.5 seconds - too slow)
- Add PBVR_VERTICAL_OFFSET = 1.3 (from PowerBeatsVR Util.cs) - PowerBeatsVR stores Y with -1.3 offset internally - JSON Y=-0.5 now displays at Y=0.8 (was displaying at -0.5) - Add tunable constants for position adjustment: - PBVR_X_SCALE / PBVR_Y_SCALE for optional scaling - PBVR_X_OFFSET / PBVR_Y_OFFSET for fine-tuning - Update _pbvr_to_es_position() to apply transformations - Update POWERBEATSVR_MAP.md with vertical offset documentation - Update tests to expect new Y offset values This fixes balls appearing too low (below floor level).
- Replace CheckBox with CheckButton for DisableTimeWarp and OnlyPowerBalls - CheckButton shows larger toggle switch on the right side (more visible in VR) - Widen controls from 477 to 600 pixels to accommodate toggle
From PowerBeatsVR GameManager.cs:
if (isPowerBall) { velocity /= 4f; }
Changes:
- Add _is_power_ball flag to notes (PowerBeatsVRMap.gd, MapLoader.gd)
- Store per-ball PowerBall flag in note.gd
- Apply purple tint to PowerBalls (_set_ball_purple)
- Update player.gd hit detection to check per-ball, not globally
- Divide velocity by 4 for PowerBalls (matches PowerBeatsVR exactly)
Behavior:
- NormalBall: 1.0 m/s minimum, normal color
- PowerBall: 2.0 m/s minimum (4x), purple color
- 'Only Power Balls' setting: converts ALL balls to PowerBalls
Documentation updated in POWERBEATSVR_MAP.md
Adds a small OmniLight3D to the left-hand hammer and enables material emission on the hammer mesh at runtime so its texture details remain visible in darker areas.
- Hide old Handle/Blade meshes on RightHand - Add hammer_smaller_hand_right instance (mirrored from left hand model) - Update RightHand collision shapes to match hammer geometry (capsule for handle + box for head) - Update controller.gd to apply emissive glow to both left and right hammers
Shows score value as 3D text that drifts from hit location. Green for perfect/full impact hits, white for partial hits. Matches PowerBeatsVR visual feedback style.
Implements a daily playtime tracking system that: - Tracks time only when music is playing during gameplay - Pauses accumulation when game is paused - Persists to JSON save file (user://saves/player_data.json) - Resets at midnight for a new day - Displays formatted time (HH:MM:SS) in main menu New files: - SaveManager.gd: Generic JSON save/load system for game progress - PlaytimeTracker.gd: Tracks playtime during gameplay sessions - test_playtime.gd: Unit tests for save/load and time formatting
- Refactor ui_song_list.gd to browse music folder structure instead of flat JSON list - Add folder navigation with '..' to go up (stops at music root) - Show all music files, grey out those without matching JSON layouts - Use filename as display name instead of loading full map data (fixes terminal spam) - Fix PowerBeatsVR paths to point to parent folder (outside src/) - Add unit tests for folder navigation, music file detection, and layout matching - Update scene to ensure PathLabel and NoSongsLabel are properly configured
- Add null check in GDScriptAudioImport.gd after AudioStreamOggVorbis.load_from_buffer() - Add null checks in ui_song_list.gd before assigning stream to beatplayer - Add null check in Game.gd before assigning stream to beat player - Remove hardcoded OGG type override to allow auto-detection of file format
- Restructure ui_song_list.tscn to add ListContainer with ScrollUpBtn/ScrollDownBtn for both Original and Custom tabs - Add scroll button logic to ui_song_list.gd: - References for scroll_up_btn and scroll_down_btn - _on_scroll_up_pressed() and _on_scroll_down_pressed() handlers that scroll by one page - _update_scroll_button_visibility() to show/hide buttons based on scroll position - Connect to scrollbar value_changed signal for dynamic visibility updates - Buttons are hidden by default and only shown when there is content above/below visible area - Add unit test test_song_list_scroll_buttons() to verify: - Scene structure with ListContainer and scroll buttons - Initial visibility state (buttons start hidden) - Signal connections for pressed events - Script methods exist
- Remove auto_height=true from SongList ItemList nodes - Reduce SongList height from 736 to 636 to leave room for scroll buttons - ItemList now scrolls internally instead of expanding to fit all items
- Add ensure_current_is_visible() call when an item is selected - Update scroll button visibility after selection - Ensures selected items are scrolled into view in both tabs
- Disable parent ScrollContainer scrolling (horizontal_scroll_mode=0, vertical_scroll_mode=0) to prevent interference with ItemList internal scrolling - Set all scroll buttons to start hidden (visible=false) in scene file - Add _ensure_selected_visible() helper with deferred call for proper layout timing - Use call_deferred for scroll updates to ensure UI is laid out - Clean up debug prints
- Remove size_flags_vertical from SongList to prevent it from expanding - Add mouse_filter=0 (STOP) to scroll buttons to capture input - Connect button_down signal instead of pressed signal for VR compatibility (VR raycast release events don't properly trigger 'pressed' signal) - Manually connect signals in _setup_ui_nodes() for reliability - Clean up debug prints
- MainMenu.gd: Disable glow/fog/procedural sky on Quest - Game.gd: Add volumetric_fog_enabled = false - Add QUEST_OPTIMIZATION.md documenting all findings Key insight: Environment effects (glow, fog, procedural sky) are the main FPS killers. SubViewport throttling causes visual bugs (menus appear stuck to head) and should NOT be used. Branch based on ecdb6d5 (before broken throttling commit).
- Fixed Game.gd to use _environment_manager.environment (not local export var) - Added solid color background, disabled glow/fog on Quest - Added GameTest debug mode to GameManager for easy FPS testing - Updated QUEST_OPTIMIZATION.md with important env variable note Both Menu and Game scenes now run at 72 FPS on Quest.
- Add comprehensive shader warmup in GameManager._ready() that preloads: - Game.tscn scene - Note/Obstacle scenes with all material variants - Ground material, environment, explosion effects - Extend StartTimer to 4 seconds on Quest for GPU settling time - Add FPS logging in Game.gd for first 10 seconds (for debugging) Results on Quest 2: - Before: 4 FPS at start, 72 FPS at 7.5 seconds - After: 18 FPS at start, 72 FPS at 2.5 seconds - When music starts (4s countdown): stable 72 FPS
Replaces TIME-based UV scrolling with a mesh-movement approach to eliminate vertex 'pumping' artifacts at lower subdivision levels. HOW IT WORKS: - Mesh physically moves toward player (positive Z) - When it moves one subdivision period, it snaps back to start - UV offset increments to compensate, keeping texture continuous - Vertices move WITH the mesh instead of sampling moving noise FEATURES: - Debug mode: set_debug_mode(true) shows height colors (red=peak, blue=valley) - Curved world: set_curved_world(true) bends terrain at distance - Reduced subdivisions from 512 to 128 (75% reduction, better performance) - All parameters documented in shader comments Files changed: - src/effects/Ground.tres: New micro conveyor shader with debug features - src/scripts/Ground.gd: Micro conveyor logic with reset_ground(), set_debug_mode() - src/scenes/Ground.tscn: Lower subdivisions, extra_cull_margin for displacement
GRID CHANGES: - Reduced uv1_scale from 4 to 2 (bigger grid squares, scroll slower) - Added grid_scale export to Ground.gd for easy tuning MOUNTAIN CHANGES: - Added noise_scale uniform (0.4 = bigger mountains) - Added noise_detail_scale + noise_detail_strength for variation - Configured FastNoiseLite with: - frequency = 0.008 (lower = larger features) - fractal_type = 2 (ridged) - fractal_octaves = 3 (more detail layers) This creates larger, more distinct mountains that don't blur together when scrolling, with natural size variation.
Added mountain_scale uniform (default 0.25) that controls how wide/long mountains appear. Lower values = wider mountains that stay on screen longer. - 1.0 = original size (small, zip past quickly) - 0.25 = 4x wider/longer (current setting) - 0.1 = 10x wider (very stretched) This is independent of the grid texture scale (uv1_scale) so you can have detailed grid textures on larger mountain features.
PROBLEM SOLVED: Traditional shader UV scrolling causes 'vertex pumping' artifacts where vertices oscillate up/down as noise values pass through fixed positions. SOLUTION: Physically move the mesh a small distance (one subdivision period), then snap back and increment a UV offset. Vertices move WITH the mesh instead of having values scroll through them. KEY MATH: - world_period = (mesh_size × mesh_scale) / subdivisions - uv_period = grid_scale / subdivisions - triplanar_offset = uv_offset × mesh_size (coordinate space conversion) FILES: - src/scripts/Ground.gd: Micro conveyor logic with scale accounting - src/effects/Ground.tres: Shader with mesh_size_z uniform for triplanar - src/scenes/GroundCurveTest.tscn: Standalone test scene (no game deps) - src/scenes/GroundChunk.tscn: Test chunk scene - docs/internals/GROUND_MICRO_CONVEYOR.md: Full documentation with math DEBUG FEATURES: - set_debug_mode(true): Height colors (red=peak, blue=valley, grid lines) - set_curved_world(true): Bend terrain at distance to hide horizon See docs/internals/GROUND_MICRO_CONVEYOR.md for detailed explanation.
- NoteExplosion.gd: Initialize vel variable to Vector3.ZERO to prevent null reference error - Game.gd: Add debug logging for map loading process - GameManager.gd: Add CustomSongTest debug mode for automated level testing - MapFactory.gd: Add detailed debug logging for format detection and layout loading These changes help diagnose level loading issues on Quest devices.
Explains how to: - Wake Quest display remotely - Take VR screenshots via ADB - Use CustomSongTest auto-start mode - Interpret debug logs - Verify files on Quest - Run full debug sessions
New DebugController enables keyboard-based remote testing: - F1: Auto-start test level - F2: Return to menu - F3: Print game state to logs - 1-5: Set difficulty - ENTER/ESC: Confirm/Back Keys can be sent via ADB: adb shell input keyevent KEYCODE_F1 Enables fully automated AI-assisted testing on Quest without physical interaction. Updated documentation with examples.
Changed shader warmup to use timer instead of process_frame await. The process_frame await would hang indefinitely if Quest display was off or app lost focus. Using a 0.5s timer ensures warmup completes even when the headset isn't being worn.
Both GameManager and MainMenu warmup routines were causing Vulkan device loss crashes. Disabling them for now - will need to investigate a safer approach later.
…ebug_test_song export - Add @export debug_test_song to GameManager.gd for configurable test song - Update CustomSongTest mode to use debug_test_song instead of hardcoded value - Update DebugController.gd to read test song from GameManager - Document remote debugging system in AGENTS.md (section 13) - Update docs/QUEST_MCP_DEBUGGING.md with generic examples Configuration is now centralized in GameManager.tscn inspector: - debug_start_scene: 'Menu', 'Game', 'GameTest', or 'CustomSongTest' - debug_test_song: filename only (e.g., 'My Song.mp3') This allows AI-assisted testing without committing hardcoded song names.
Creates the missing dependencies for GroundCurveTest.tscn and GroundChunk.tscn: - src/scripts/GroundMicroConveyor.gd: Standalone test script for the micro conveyor belt system with debug mode (press D to toggle height colors) - src/scripts/GroundChunk.gd: Simple static chunk script for testing - src/effects/GroundCurved.tres: Shader material with curved world enabled and debug_height_colors=true by default DEBUG MODE FEATURES: - Red = peaks (high terrain) - Green = middle elevation - Blue = valleys (low terrain) - White grid lines show UV cell boundaries Test scenes can now run independently for debugging terrain scrolling without requiring the full game infrastructure.
- Changed uv_offset_z to instance uniform in shader for per-mesh UV offsets - Added GroundShapeB mesh to Ground.tscn, positioned 64 units ahead of GroundShapeA - Rewrote Ground.gd with dual mesh teleport logic: - Both meshes move continuously in +Z direction - When mesh goes behind player (z > 64), teleport 128 units back - Each mesh maintains its own UV offset via instance shader parameter - Updated documentation with new dual mesh system explanation This eliminates the motion discontinuity that caused SpaceWarp artifacts. The old system snapped back every 0.5 units (visible), the new system teleports only when fully behind the player (invisible to SpaceWarp).
…bugController - Removed duplicate script override on FPSOverlay node (was setting script that scene already has) - Changed FPSOverlay from Node3D to use the scene's default type - Added UID reference to DebugController script for better resource tracking
The uv_offset_z parameter was set to 0.0 (default) and is not actively used. Removing to clean up the material resource.
Based on GodotCIBuildDemo, this adds: - Windows, Linux, and Android Quest CI export presets - GitHub Actions workflow (build-on-push.yml) for: - Windows x86_64 builds - Linux x86_64 builds - Android Quest APK builds - Build output directories (src/build/) for CI - Automatic GitHub releases on version tags Local development continues to use: - 'Meta Quest 2' preset -> out/tempovr.apk - tools/deploy_quest.py for device deployment CI builds use separate presets with different output paths to avoid conflicts with local development.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Multi-Platform CI/CD Workflow
Based on GodotCIBuildDemo, this adds:
Features
v*)Build Separation Strategy
out/tempovr.apkbuild/{platform}/*Local development via
tools/deploy_quest.pyremains unchanged.Changes
src/export_presets.cfgwith Windows, Linux, and Android Quest CI presets.github/workflows/build-on-push.ymlworkflow.github/workflows/main.yml(Godot 3.x)src/build/output directories with.gitignoreTested
Known Issues
chickensoft-games/setup-godot