最近在做COTA的音效,进一步深入了kv3文件,kv3比起kv2还是比较好写,而且代码相对会简洁很多,而且,KV3可以很方便地转成json(因为格式太像了),如果我们想用啥现成的工具来管理kv3文件的话,只需要自己写一个简单的kv3-json转换器就可以了。
言归正传,首先来说一下kv3文件。
就目前来讲,dota2自定义游戏中使用到kv3文件的部分包括了 vmdl, vpcf, vsndevts,也就是模型、特效和音效文件,此外还有custom_net_tables.txt使用的也是kv3文件,更多的部分(例如npc文件夹的那些东西)能不能用kv3,我还没有尝试过,欢迎去试试。
1. KV3的语法格式
kv3是V社开发的特殊数据格式,非常像json,增加的功能是允许二进制编码,版本控制和数据注释等等,此外还有额外的语法支持,比如多行注释和多行字符串等。
1.1 文件头
每个KV3文件都需要一个文件头,里面包含了标识编码和格式的guid,对于大多数人来讲,只需要记住创建文件的时候复制粘贴这一行到文件头就可以了。
<!-- kv3 encoding:text:version{e21c7f3c-8a33-41c5-9977-a76d3a32aa0d} format:generic:version{7412167c-06e9-4698-aff2-e63eb59037e7} -->
1.2 语法
语法很简单,一个文件就足以说明所有问题
<!-- kv3 encoding:text:version{e21c7f3c-8a33-41c5-9977-a76d3a32aa0d} format:generic:version{7412167c-06e9-4698-aff2-e63eb59037e7} -->
{
boolValue = false // 布尔值
intValue = 128 // 整数
doubleValue = 64.000000 // 浮点数
stringValue = "hello world" // 字符串
stringThatIsAResourceReference = resource:"particles/items3_fx/star_emblem.vpcf" // 这个是带有resource flag前缀的特殊用法
multiLineStringValue = """
First line of a multi-line string literal.
Second line of a multi-line string literal.
"""
// 多行字符串
arrayValue =
[
1,
2,
]
// 数组,注意各个元素之间有逗号
objectValue =
{
n = 5
s = "foo"
}
// Object,注意各个元素之间没有逗号
/*
这是一个多行注释
*/
}
1.3 多行字符串的特殊说明
在kv3中写多行字符串,需要用三个括号开始,之后立马开始一个新行,在多行字符串结束之后,需要开始一个新行,之后写三个括号。这两个新行都是必须的。在这六个括号之间的所有字符,都会被直接读取,而不解析(也就是说\n之类的并不会被解释为换行。)
2. kv3自定义音效
dota2的自定义音效的使用需要两种类型的文件,一个是位于soundevents中的vsndevts kv3文件,一个是位于sounds文件夹中的音频文件(mp3和wav格式的音乐文件都是支持的,其他类型的文件没有测试)。音频文件就不说了,只要放着就可以了,在完成vsndevts的编辑之后,音频文件将会被自动编译成vsnd文件。
这里先放一个示例文件,再来细说:
<!-- kv3 encoding:text:version{e21c7f3c-8a33-41c5-9977-a76d3a32aa0d} format:generic:version{7412167c-06e9-4698-aff2-e63eb59037e7} -->
{
Rock.ui_startup =
{
type = "dota_update_ui_startup"
vsnd_files =
[
"sounds/ui_startup_01.vsnd",
"sounds/ui_startup_02.vsnd",
]
volume = "1.000000"
weights =
[
"1",
"1",
]
}
}
只要这么定义之后,我们就可以在lua或者js文件中来使用EmitSound的各类API来使用 "Rock.ui_startup"来播放我们放在sounds文件夹中的ui_startup_01.mp3或者ui_startup_02.mp3了。
解释一下几个参数:
1、 type 声音的事件类型,这个事件是很重要的,目前所有可能的值包括
dota_src1_2d // 2D音频
dota_src1_3d // 3D音频
battle_end_countdown // 战斗结束的倒数计时
dota_gamestart_horn // 游戏开始的号角
dota_update_vo_clamped_panning
dota_update_vo_spatial_stereo
dota_update_vo_switch
dota_update_vo_default
dota_update_vo_clamped
dota_update_vo_announcer // 几种类型的voice
dota_update_ganked_music
dota_update_music_3layer
dota_update_battle_music
dota_update_battle_end
dota_update_hero_select
dota_update_default
dota_update_killed
dota_update_smoke
dota_update_smoke_end
dota_update_smoke_end_hero
dota_update_roshan
dota_update_roshan_end
dota_music_respawn
dota_update_countdown
dota_update_opvar_track
dota_music_stinger_stoptracks
dota_ui_start_multi
dota_ui_limit_time
dota_update_ui_startup
dota_update_ui_main
dota_stop_loop_ui
dota_voip
dota_limit_speakers_ui
dota_limit_speakers_inv
dota_limit_speakers_spectui
我们一般来说只需要用到dota_src1_2d
或者dota_src1_3d
类型的音频就可以了,要记住的一点就是,如果你突然不知道要怎么定义了,就到官方的vsndevts文件中去搜索同样类型的文件,仿照着抄就可以了。
2、声音的属性定义
// 属性名 范例值 说明
soundlevel "81.000000" 声音强度
distance_max "2500.000000" 最大范围,3d音效使用
spread_radius "6000.000000" 扩展范围
rand_delay_min "1.000000" 随机开始延迟的最小和最大值
rand_delay_max "5.000000"
volume 0-1 声音的音量
volume_fade_in "1.500000" 淡入时间
volume_fade_out "1.500000" 淡出时间
volume_falloff_min "10.000000" 减弱距离
volume_falloff_max "1000.000000"
volume_rand_min "-0.149902" 随机的音量偏移的最小和最大值
volume_rand_max "0.149902"
pitch 0-1 音调,属性和音量类似
pitch_rand_min "-0.010000"
pitch_rand_max "0.010000"
vsnd_files "sounds/vo/announcer/announcer_now_select.vsnd" // 定义音乐文件,不管你的音乐是啥扩展名,一概写成vsnd就可以
vsnd_files
[
"sounds/vo/announcer/announcer_now_select1.vsnd",
"sounds/vo/announcer/announcer_now_select2.vsnd"
]
weights
[
1,
1
]
// 随机在多个音乐之间选择一个,用weights来定义不同的文件的出现概率
start_point "2.000000"
end_point "9.000000"
restart_time "41.599998"
stop_at_time "40.000000"
// 循环播放的音频的起始点、结束点、重新开始的时间
position "[5545.06, 4998.54, 1494]"
// 定义播放的位置
// 下面的这些属性不常用,有兴趣的可以继续往下看
limiter_on
0 1
limiter_event_name "ui.spect_pickup_in"
limiter_max "1.000000"
limiter_match_entity "0.000000"
limiter_match_type 0 1
limiter_match_substring "0.000000"
limiter_match_entity "1.000000"
memory_type 0
volume_remap_x1 "0.250000"
volume_remap_x2 "0.500000"
volume_remap_x3 "0.750000"
volume_remap_x4 "1.000000"
volume_remap_y1 "0.250000"
volume_remap_y2 "0.500000"
volume_remap_y3 "0.750000"
volume_remap_y4 "1.000000"
remap_vol_x1 "0.800000"
remap_vol_y1 "0.250000"
remap_vol_x2 "0.900000"
remap_vol_y2 "0.500000"
remap_vol_x3 "1.000000"
remap_vol_y3 "1.000000"
remap_vol_x4 "1.000000"
remap_vol_y4 "1.000000"
volume_move_vol "0.010000"
volume_move_vel "300.000000"
volume_move_filter_vel "1.000000"
pitch_remap_x1 "0.250000"
pitch_remap_x2 "0.500000"
pitch_remap_x3 "0.750000"
pitch_remap_x4 "1.000000"
pitch_remap_y1 "0.250000"
pitch_remap_y2 "0.500000"
pitch_remap_y3 "0.750000"
pitch_remap_y4 "1.000000"
remap_intensity_x1 "0.000000"
remap_intensity_y1 "1.000000"
remap_intensity_x2 "0.200000"
remap_intensity_y2 "1.000000"
remap_intensity_x3 "0.670000"
remap_intensity_y3 "1.000000"
remap_intensity_x4 "1.000000"
remap_intensity_y4 "0.500000"
track "explore2"
random_delay_opvar "laning_01_random_delay_opvar"
use_event_data
laning_master
mixgroup "HeroPick"
mixgroup "Weapons"
mixgroup "Stingers"
mixgroup "Ping"
mixgroup "All"
mixgroup "Ultimates"
mixgroup "Physics"
mixgroup "Ambient"
mixgroup "Creeps"
stops_others 0 or 1
block_name "ui_generic_button_click"
block_time "0.250000"
block_time2 "0.750000"
block_duration "1.500000"
soundevent_01 "ui_menu_fadeout"
soundevent_02 "ui_select_md"
use_01 "1.000000"
use_02 "1.000000"
use_03 "1.000000"
stop_entry_name "random_wheel_spin"
opvar_field_name "dota_random_wheel_speed"
opvar_stack_name "dota_opvars"
opvar_operator_name "opvars"
event_type
0 1 2 3 4