最佳实践
本页面提供 RCT Python 脚本开发的最佳实践和注意事项。
开发建议
1. 始终检查返回值
get_current_chart() 可能返回 None,务必检查:
chart = get_current_chart()
if not chart:
print("错误:无法获取谱面")
return
# 继续处理...
2. 参数验证
在使用 sys.argv 前检查长度和类型:
import sys
if len(sys.argv) < 3:
print("错误:缺少参数")
return
try:
value = float(sys.argv[1])
except ValueError:
print("错误:参数必须是数字")
return
3. 使用异常处理
捕获可能的错误,提供友好的错误信息:
try:
chart = load_chart_from_file("path/to/chart.txt")
# 处理谱面...
except FileNotFoundError:
print("错误:文件不存在")
except Exception as e:
print(f"错误:{e}")
4. 输出详细信息
使用 print() 输出处理结果,信息会显示在 RCT 状态栏:
print(f"处理完成:保留了 {len(chart.notes)} 个音符")
print(f"时间范围:{min_time}ms - {max_time}ms")
5. 保存前排序
修改音符后调用 chart.sort() 确保顺序正确:
chart.add_note(Note.TAP, 1000, 45)
chart.add_note(Note.TAP, 500, 90)
chart.sort() # 重要!确保按时间排序
save_current_chart(chart)
6. 备份重要谱面
在测试新脚本前先备份谱面文件:
from rct_api import get_current_chart, save_chart_to_file
chart = get_current_chart()
if chart:
# 创建备份
save_chart_to_file(chart, "backup/chart_backup.txt")
# 然后进行修改...
代码规范
文件结构
推荐的脚本文件结构:
# Description: 脚本描述
# Args: 参数说明(可选)
# UI: UI配置(可选)
import sys
import json
from rct_api import get_current_chart, save_current_chart, Note
def main():
"""主函数"""
# 参数检查
if len(sys.argv) < 2:
print("错误:缺少参数")
return
# 获取谱面
chart = get_current_chart()
if not chart:
print("错误:无法获取谱面")
return
# 处理逻辑
try:
# ... 你的代码 ...
# 保存
save_current_chart(chart)
print("处理完成")
except Exception as e:
print(f"错误:{e}")
if __name__ == "__main__":
main()
命名规范
- 文件名:使用小写字母和下划线,如
shift_time.py - 函数名:使用小写字母和下划线,如
process_notes() - 变量名:使用描述性名称,如
start_time而不是st
注释规范
# 单行注释:说明代码意图
"""
多行注释或文档字符串:
详细说明函数功能、参数和返回值
"""
def filter_by_time(chart, start, end):
"""
按时间范围过滤音符
Args:
chart: Chart 对象
start: 起始时间(毫秒)
end: 结束时间(毫秒)
"""
chart.filter_notes_by_time(start, end)
性能优化
1. 避免重复操作
不推荐:
for i in range(len(chart.notes)):
note = chart.notes[i]
# 处理...
推荐:
for note in chart.notes:
# 处理...
2. 批量处理
不推荐:
for i in range(100):
chart.add_note(Note.TAP, i * 1000, 45)
chart.sort() # 每次都排序,效率低
save_current_chart(chart)
推荐:
for i in range(100):
chart.add_note(Note.TAP, i * 1000, 45)
chart.sort() # 只排序一次
save_current_chart(chart)
3. 使用列表推导式
不推荐:
filtered_notes = []
for note in chart.notes:
if note.type == Note.TAP:
filtered_notes.append(note)
chart.notes = filtered_notes
推荐:
chart.notes = [note for note in chart.notes if note.type == Note.TAP]
调试技巧
1. 输出调试信息
print(f"[DEBUG] 音符数量: {len(chart.notes)}")
print(f"[DEBUG] 第一个音符: type={chart.notes[0].type}, time={chart.notes[0].time}")
2. 分步测试
将复杂操作分解为多个步骤,逐步测试:
# 步骤 1:只读取和显示信息
chart = get_current_chart()
print(f"音符数量: {len(chart.notes)}")
# 步骤 2:添加少量测试数据
# chart.add_note(Note.TAP, 1000, 45)
# save_current_chart(chart)
# 步骤 3:完整功能
# ... 完整代码 ...
3. 使用条件导入
在开发时可以添加本地测试代码:
DEBUG = True # 开发时设为 True
if DEBUG:
print("[DEBUG] 进入调试模式")
# 使用测试数据...
else:
chart = get_current_chart()
常见错误
1. 忘记检查 None
❌ 错误:
chart = get_current_chart()
print(len(chart.notes)) # 如果 chart 是 None 会报错
✅ 正确:
chart = get_current_chart()
if chart:
print(len(chart.notes))
else:
print("错误:无法获取谱面")
2. 参数类型错误
❌ 错误:
offset = sys.argv[1] # 这是字符串
chart.shift_time(offset) # 需要数字
✅ 正确:
offset = float(sys.argv[1]) # 转换为浮点数
chart.shift_time(offset)
3. 忘记排序
❌ 错误:
chart.add_note(Note.TAP, 3000, 90)
chart.add_note(Note.TAP, 1000, 45)
save_current_chart(chart) # 顺序混乱
✅ 正确:
chart.add_note(Note.TAP, 3000, 90)
chart.add_note(Note.TAP, 1000, 45)
chart.sort() # 排序
save_current_chart(chart)
4. 修改列表时的迭代问题
❌ 错误:
for note in chart.notes:
if note.type == Note.BOMB:
chart.notes.remove(note) # 迭代时修改列表
✅ 正确:
chart.notes = [note for note in chart.notes if note.type != Note.BOMB]
注意事项
时间单位
- 统一使用毫秒 (ms) 作为时间单位
- 角度范围为 0-360 度
# 1 秒 = 1000 毫秒
one_second = 1000
# 半圈 = 180 度
half_circle = 180
自定义 UI 配置
- JSON 配置必须在同一行(单行注释格式)
- 或使用三引号字符串格式(推荐)
- 所有
default_value都是字符串格式
# 单行格式
# UI: {"fields":[{"name":"value","field_type":"number","label":"值:","default_value":"10","options":[]}]}
# 或三引号格式(推荐)
"""UI:{
"fields": [...]
}"""
文件命名
- 不要使用中文或特殊字符
- 使用小写字母和下划线
- 使用描述性名称
保存操作
- 修改谱面后务必调用
save_current_chart()保存 - 保存操作无法撤销
- 重要谱面请先备份
安全建议
1. 数据验证
始终验证用户输入:
if start_time < 0:
print("错误:起始时间不能为负数")
return
if end_time <= start_time:
print("错误:结束时间必须大于起始时间")
return
2. 边界检查
if len(chart.notes) == 0:
print("警告:谱面没有音符")
return
if len(chart.bpm) == 0:
print("警告:谱面没有 BPM 信息")
return
3. 限制操作范围
# 限制生成的音符数量
MAX_NOTES = 10000
if count > MAX_NOTES:
print(f"错误:音符数量不能超过 {MAX_NOTES}")
return
进阶技巧
1. 使用配置文件
import json
# 读取配置
with open("config.json", "r") as f:
config = json.load(f)
default_bpm = config.get("default_bpm", 120)
2. 命令行参数解析
使用 argparse 实现更友好的参数处理:
import argparse
parser = argparse.ArgumentParser(description="音符时间平移工具")
parser.add_argument("offset", type=float, help="时间偏移量(毫秒)")
parser.add_argument("--verbose", action="store_true", help="显示详细信息")
args = parser.parse_args()
offset = args.offset
3. 模块化设计
将常用功能封装成函数:
def validate_time_range(start, end):
"""验证时间范围"""
if start < 0 or end < 0:
return False, "时间不能为负数"
if end <= start:
return False, "结束时间必须大于起始时间"
return True, ""
# 使用
valid, error = validate_time_range(start, end)
if not valid:
print(f"错误:{error}")
return