20 KiB
Code of Conduct - Skelly Project
Overview
Coding standards and development practices for the Skelly project. These guidelines help developers contribute effectively while maintaining code quality and project consistency.
Core Principles
1. Code Clarity Over Cleverness
- Write code that is easy to read
- Use descriptive variable and function names
- Prefer explicit code over "clever" solutions
- Comment complex logic and business rules
2. Consistency First
- Follow existing code patterns
- Use same naming conventions throughout
- Maintain consistent indentation and formatting
- Follow Godot's GDScript style guide
3. Incremental Development
- Make small, focused commits
- Test changes before committing
- Don't break existing functionality
- Use debug system to verify changes
GDScript Coding Standards
Naming Conventions
📋 Quick Reference: For complete naming convention details, see the Naming Convention Quick Reference section below.
# Variables and functions: snake_case
var player_health: int = 100
func calculate_damage() -> int:
# Constants: SCREAMING_SNAKE_CASE
const MAX_HEALTH := 100
const TILE_SPACING := 54
# Classes: PascalCase
class_name PlayerController
# Scene files (.tscn) and Script files (.gd): PascalCase
# MainMenu.tscn, MainMenu.gd
# Match3Gameplay.tscn, Match3Gameplay.gd
# TestAudioManager.gd (test files)
# Signals: past_tense
signal health_changed
signal game_started
# Private functions: prefix with underscore
func _ready():
func _initialize_grid():
File Organization
# 1. extends statement
extends Node2D
# 2. class_name (if applicable)
class_name Match3Controller
# 3. Constants
const GRID_SIZE := Vector2i(8, 8)
# 4. Signals
signal match_found(tiles: Array)
# 5. Variables
var grid := []
@onready var debug_ui = $DebugUI
# 6. Functions (lifecycle first, then custom)
func _ready():
func _process(delta):
func custom_function():
Documentation Requirements
# Required for all public functions
func set_gem_pool(gem_indices: Array) -> void:
"""Set specific gem types as the active pool"""
_update_all_tiles_gem_pool(gem_indices)
print("Set gem pool to: ", gem_indices)
# Document complex algorithms
func _get_match_line(start: Vector2i, dir: Vector2i) -> Array:
"""Find all matching tiles in a line from start position in given direction"""
var line = [grid[start.y][start.x]]
var type = grid[start.y][start.x].tile_type
# Implementation...
Project-Specific Guidelines
Scene Management
- All scene transitions go through
GameManager - Never use
get_tree().change_scene_to_file()directly - Define scene paths as constants in GameManager
# ✅ Correct
GameManager.start_match3_game()
# ❌ Wrong
get_tree().change_scene_to_file("res://scenes/game/Game.tscn")
Autoload Usage
- Use autoloads for global state only
- Don't access autoloads from deeply nested components
- Pass data through signals or direct references when possible
# ✅ Correct - using signals
signal settings_changed
SettingsManager.volume_changed.connect(_on_volume_changed)
# ✅ Also correct - direct access for global state
var current_language = SettingsManager.get_setting("language")
# ❌ Wrong - tight coupling
func some_deep_function():
SettingsManager.set_setting("volume", 0.5) # Too deep in call stack
Debug System Integration
- Always connect to DebugManager signals for debug UI
- Use structured logging instead of plain print statements
- Remove temporary debug logs before committing (unless permanently useful)
# ✅ Correct debug integration
func _connect_to_global_debug() -> void:
DebugManager.debug_ui_toggled.connect(_on_debug_ui_toggled)
debug_ui.visible = DebugManager.is_debug_ui_visible()
# ✅ Good structured logging
DebugManager.log_debug("Debug UI toggled to: " + str(visible), "Match3")
DebugManager.log_info("Initialized " + str(tiles.size()) + " tiles", "TileSystem")
# ❌ Bad logging practices
print("test") # Not descriptive, use structured logging
print(some_variable) # No context, use proper log level
Logging Standards
- Use
DebugManager.log_*()functions instead ofprint()orpush_error() - Choose log levels based on message importance and audience
- Include categories to organize log output by system/component
- Format messages with clear, descriptive text and relevant context
# ✅ Correct logging usage
DebugManager.log_info("Game scene loaded successfully", "SceneManager")
DebugManager.log_warn("Audio file not found, using default", "AudioManager")
DebugManager.log_error("Failed to save settings: " + error_message, "Settings")
# ❌ Wrong approaches
print("loaded") # Use DebugManager.log_info() with category
push_error("error") # Use DebugManager.log_error() with context
if debug_mode: print("debug info") # Use DebugManager.log_debug()
Asset Management
- Document every asset in
assets/sources.yaml - Include source information, license details, and attribution
- Document modifications made to original assets
- Verify license compatibility before adding assets
- Update sources.yaml in same commit as adding asset
# ✅ Correct asset addition workflow
# 1. Add asset file to appropriate assets/ subdirectory
# 2. Update assets/sources.yaml with complete metadata
# 3. Commit both files together with descriptive message
# Example sources.yaml entry:
# audio:
# sfx:
# "button_click.wav":
# source: "https://freesound.org/people/user/sounds/12345/"
# license: "CC BY 3.0"
# attribution: "Button Click by SoundArtist"
# modifications: "Normalized volume, trimmed silence"
# usage: "UI button interactions"
Error Handling
- Check if resources loaded successfully
- Use
DebugManager.log_error()for critical failures - Provide fallback behavior when possible
- Include meaningful context in error messages
# Good error handling with structured logging
func load_scene(path: String) -> void:
var packed_scene := load(path)
if not packed_scene or not packed_scene is PackedScene:
DebugManager.log_error("Failed to load scene at: %s" % path, "SceneLoader")
return
DebugManager.log_info("Successfully loaded scene: %s" % path, "SceneLoader")
get_tree().change_scene_to_packed(packed_scene)
Memory Management Standards
Critical Rules:
- Always use
queue_free()instead offree()for node cleanup - Wait for frame completion after queueing nodes for removal
- Clear references before cleanup to prevent access to freed memory
- Connect signals properly for dynamically created nodes
# ✅ Correct memory management
for child in children_to_remove:
child.queue_free()
await get_tree().process_frame # Wait for cleanup
# ❌ Dangerous pattern (causes crashes)
for child in children_to_remove:
child.free() # Immediate deletion can crash
Why This Matters: Using free() causes immediate deletion which can crash if the node is still referenced elsewhere. queue_free() waits until it's safe to delete.
See Also: ARCHITECTURE.md for architectural rationale.
Input Validation Standards
All user inputs must be validated before processing.
Validation Requirements:
- Type checking: Verify input types before processing
- Bounds checking: Validate numeric ranges and array indices
- Null checking: Handle null and empty inputs gracefully
- Whitelist validation: Validate against known good values
# ✅ Proper input validation
func set_volume(value: float, setting_key: String) -> bool:
# Whitelist validation
if not setting_key in ["master_volume", "music_volume", "sfx_volume"]:
DebugManager.log_error("Invalid volume setting key: " + str(setting_key), "Settings")
return false
# Bounds checking
var clamped_value = clamp(value, 0.0, 1.0)
if clamped_value != value:
DebugManager.log_warn("Volume value clamped from %f to %f" % [value, clamped_value], "Settings")
SettingsManager.set_setting(setting_key, clamped_value)
return true
# ✅ Grid movement validation
func _move_cursor(direction: Vector2i) -> void:
# Bounds checking
if abs(direction.x) > 1 or abs(direction.y) > 1:
DebugManager.log_error("Invalid cursor direction: " + str(direction), "Match3")
return
# Null/empty checking
if not grid or grid.is_empty():
DebugManager.log_error("Grid not initialized", "Match3")
return
# Process validated input
var new_pos = cursor_pos + direction
# ... continue with validated data
See Also: ARCHITECTURE.md for architectural decision rationale.
Code Quality Checklist
Before committing code, verify:
- All user inputs validated (type, bounds, null checks)
- Error handling with fallback mechanisms implemented
- Memory cleanup uses
queue_free()notfree() - No global static state introduced
- Proper logging with categories and appropriate levels
- Race condition protection for async operations
- Input actions defined in project input map
- Resources loaded with null/type checking
- Documentation updated for new public APIs
- Test coverage for new functionality
Git Workflow
Commit Guidelines
- Use clear, descriptive commit messages
- Start with a verb in imperative mood
- Keep first line under 50 characters
- Add body if needed for complex changes
# Good commit messages
Add gem pool management to match-3 system
Fix debug UI visibility toggle issue
Update documentation for new debug system
# Bad commit messages
fix bug
update
wip
Branch Management
- Create feature branches for new functionality
- Use descriptive branch names:
feature/debug-ui,fix/tile-positioning - Keep branches focused on single features
- Delete branches after merging
Before Committing
- Test your changes in the Godot editor
- Check that existing functionality still works
- Use the debug system to verify your implementation
- Run the project and navigate through affected areas
- Remove temporary debug code
Code Review Guidelines
For Reviewers
- Focus on code clarity and maintainability
- Check for adherence to project patterns
- Verify that autoloads are used appropriately
- Ensure debug integration is properly implemented
- Test the changes yourself
For Contributors
- Explain complex logic in commit messages or comments
- Provide context for why changes were made
- Test edge cases and error conditions
- Ask questions if project patterns are unclear
Testing Approach
Manual Testing Requirements
- Test in Godot editor with F5 run
- Verify debug UI works with F12 toggle
- Check scene transitions work
- Test on different screen sizes (mobile target)
- Verify audio and settings integration
Debug System Testing
- Ensure debug panels appear/disappear correctly
- Test all debug buttons and controls
- Verify debug state persists across scene changes
- Check debug code doesn't affect release builds
Naming Convention Quick Reference
🎯 Single Source of Truth: This section contains all naming conventions for the Skelly project. All other documentation files reference this section to avoid duplication and ensure consistency.
1. GDScript Code Elements
# Variables and functions: snake_case
var player_health: int = 100
func calculate_damage() -> int:
# Constants: SCREAMING_SNAKE_CASE
const MAX_HEALTH := 100
const TILE_SPACING := 54
# Classes: PascalCase
class_name PlayerController
# Signals: past_tense_with_underscores
signal health_changed
signal game_started
signal match_found
# Private functions: prefix with underscore
func _ready():
func _initialize_grid():
2. File Naming Standards
Script and Scene Files
# ✅ Correct: All .gd and .tscn files use PascalCase
MainMenu.tscn / MainMenu.gd
Match3Gameplay.tscn / Match3Gameplay.gd
ClickomaniaGameplay.tscn / ClickomaniaGameplay.gd
ValueStepper.tscn / ValueStepper.gd
# Test files: PascalCase with "Test" prefix
TestAudioManager.gd
TestGameManager.gd
TestMatch3Gameplay.gd
# ❌ Wrong: Old snake_case style (being migrated)
main_menu.tscn / main_menu.gd
TestAudioManager.gd
Rules:
- Scene files (.tscn) must match their script file name exactly
- All new files must use PascalCase
- Test files use "Test" prefix + PascalCase
- Autoload scripts follow PascalCase (GameManager.gd, AudioManager.gd)
3. Directory Naming Conventions
Source Code Directories
# Source directories: snake_case
src/autoloads/
scenes/game/gameplays/
scenes/ui/components/
tests/helpers/
# Root directories: lowercase
docs/
tests/
tools/
data/
Asset Directories
# Asset directories: kebab-case
assets/audio-files/
assets/ui-sprites/
assets/game-textures/
assets/fonts/
localization/
4. Resource and Configuration Files
# Configuration files: lowercase with dots
project.godot
gdlintrc
.gdformatrc
.editorconfig
export_presets.cfg
# Godot resource files (.tres): PascalCase
data/DefaultBusLayout.tres
data/PlayerSaveData.tres
scenes/ui/DefaultTheme.tres
# Asset metadata: kebab-case
assets/asset-sources.yaml
assets/audio-files/audio-sources.yaml
assets/ui-sprites/sprite-sources.yaml
# Development files: kebab-case
requirements.txt
development-tools.md
5. Asset File Naming
# Audio files: kebab-case in kebab-case directories
assets/audio-files/background-music.ogg
assets/audio-files/ui-sounds/button-click.wav
assets/audio-files/game-sounds/match-sound.wav
# Visual assets: kebab-case
assets/ui-sprites/main-menu-background.png
assets/game-textures/gem-blue.png
assets/fonts/main-ui-font.ttf
# Import settings: match the original file
background-music.ogg.import
button-click.wav.import
Asset Rules:
- All asset files use kebab-case
- Organized in kebab-case directories
- Import files automatically match asset names
- Document all assets in
asset-sources.yaml
6. Git Workflow Conventions
Branch Naming
# Feature branches: feature/description-with-hyphens
feature/new-gameplay-mode
feature/settings-ui-improvement
feature/audio-system-upgrade
# Bug fixes: fix/description-with-hyphens
fix/tile-positioning-bug
fix/save-data-corruption
fix/debug-menu-visibility
# Refactoring: refactor/component-name
refactor/match3-input-system
refactor/autoload-structure
# Documentation: docs/section-name
docs/code-of-conduct-update
docs/api-documentation
Commit Message Format
# Format: <type>: <description>
# Examples:
feat: add dark mode toggle to settings menu
fix: resolve tile swap animation timing issue
docs: update naming conventions in code of conduct
refactor: migrate print statements to DebugManager
test: add comprehensive match3 validation tests
7. Quick Reference Summary
| File Type | Convention | Example |
|---|---|---|
| GDScript Files | PascalCase | MainMenu.gd, AudioManager.gd |
| Scene Files | PascalCase | MainMenu.tscn, Match3Gameplay.tscn |
| Test Files | Test + PascalCase | TestAudioManager.gd |
| Variables/Functions | snake_case | player_health, calculate_damage() |
| Constants | SCREAMING_SNAKE_CASE | MAX_HEALTH, TILE_SPACING |
| Classes | PascalCase | class_name PlayerController |
| Signals | past_tense | health_changed, game_started |
| Directories | snake_case (src) / kebab-case (assets) | src/autoloads/, assets/audio-files/ |
| Assets | kebab-case | background-music.ogg, gem-blue.png |
| Config Files | lowercase.extension | project.godot, .gdformatrc |
| Branches | type/kebab-case | feature/new-gameplay, fix/tile-bug |
✅ Status: All major file naming inconsistencies have been resolved. The project now follows consistent PascalCase naming for all .gd and .tscn files.
8. File Renaming Migration Guide
When renaming files to follow conventions:
Step-by-step procedure:
- Use Git rename:
git mv old_file.gd NewFile.gd(preserves history) - Update .tscn references: Modify script path in scene files
- Update code references: Search and replace all
preload()andload()statements - Update project.godot: If file is referenced in autoloads or project settings
- Update documentation: Search all .md files for old references
- Update test files: Modify any test files that reference the renamed file
- Run validation: Execute
gdlint,gdformat, and project tests - Verify in editor: Load scenes in Godot editor to confirm everything works
Tools for validation:
python tools/run_development.py --test- Run all testspython tools/run_development.py --lint- Check code qualitypython tools/run_development.py --format- Ensure consistent formatting
Common Mistakes to Avoid
Architecture Violations
# Don't bypass GameManager
get_tree().change_scene_to_file("some_scene.tscn")
# Don't hardcode paths
var tile = load("res://scenes/game/gameplays/Tile.tscn")
# Don't ignore null checks
var node = get_node("SomeNode")
node.do_something() # Could crash if node doesn't exist
# Don't create global state in random scripts
# Use autoloads instead
Asset Management Violations
# Don't add assets without documentation
# Adding audio/new_music.mp3 without updating sources.yaml
# Don't use assets without verifying licenses
# Using copyrighted music without permission
# Don't modify assets without documenting changes
# Editing sprites without noting modifications in sources.yaml
# Don't commit assets and documentation separately
git add assets/sprites/new_sprite.png
git commit -m "add sprite" # Missing sources.yaml update
# Correct approach
git add assets/sprites/new_sprite.png assets/sources.yaml
git commit -m "add new sprite with attribution"
Performance Issues
# Don't search nodes repeatedly
func _process(delta):
var ui = get_node("UI") # Expensive every frame
# Cache node references
@onready var ui = $UI
func _process(delta):
ui.update_display() # Much better
Debug System Misuse
# Don't create separate debug systems
var my_debug_enabled = false
print("debug: " + some_info) # Don't use plain print()
# Use the global debug and logging systems
if DebugManager.is_debug_enabled():
show_debug_info()
DebugManager.log_debug("Debug information: " + some_info, "MyComponent")
Learning Resources
Godot-Specific
- GDScript Style Guide
- Godot Best Practices
- Project documentation in
docs/map.md
General Programming
- Clean Code principles
- SOLID principles (adapted for game development)
- Git best practices
Getting Help
When Stuck
- Check existing code for similar patterns
- Review project documentation (
docs/) - Use the debug system to understand current state
- Ask specific questions about project architecture
- Test your understanding with small experiments
Code Review Process
- Submit pull requests early and often
- Ask for feedback on approach before implementing large features
- Be open to suggestions and alternative approaches
- Learn from review feedback for future contributions
Summary
This code of conduct emphasizes:
- Clarity: Write code that others can understand
- Consistency: Follow established patterns
- Integration: Use the project's systems properly
- Learning: Continuously improve your skills
Remember: It's better to ask questions and write clear, simple code than to guess and create complex, hard-to-maintain solutions. The goal is to contribute effectively to a codebase that will grow and evolve over time.