本帖最后由 vegetto 于 2023-1-10 10:39 编辑
前言:什么是骨骼拼接,这个概念由我提出,将2个或以上的人将他们任意的骨骼坐标可以重合一起在任何动作行为下保持高度协调的相对运动。比如我在创意工坊小有知名度的“男上加男”。实际上可以应用更广,比如做武侠,一个人手拎着一个人的脚甩,或者是巨人直接把人抓手里蹂躏。如果不用我这个理论,那这类功能另外一种实现方法只能把行为全部由预设特制的动作接管,这种控制的自由度以及有效碰撞的处理便不太方便。特别的,还可以用于制作三头六臂,又或者将其中一个人体配合特定动作形成鞭子绑定,也就是说,利用人体部分绑定拼接来来用于人的骨骼动画组合,使得人不一定是人,也可以是一种工具。当然回归主题,本贴先讲其中一个用例。
基础理论支撑:
1.采用的关键op
2076 # (agent_get_bone_position, <position_no>, <agent_no>, <bone_no>, [<local_or_global>]), #op详解,请openbrf打开有骨骼的brf或者最右上角选项卡点击扩展开有个“编辑样本数据”,会打开一个reference.BRF,里面有参考骨骼,你会发现骨骼资源的面板数据上有骨骼的编号和名称,点击其中一个骨骼名称对应模型预览相应的骨骼碰撞会高亮,这样你就知道<agent_no>这个人的骨骼编号<bone_no>填的对应编号的骨骼大概是个什么位置和范围。最后通过<position_no>得到具体位置,[<local_or_global>]坐标本地全局类型一般1填的多一些。然后要注意的是这个得到的是骨骼大小在skin.PY定义为默认1.00000的骨骼坐标,如果一些种族骨骼大小不是1,则需要进行换算。
1762 # (agent_set_no_dynamics, <agent_id>, <value>), #这个我在其他帖子讲过了,可以让agent人不受重力坐标,设置在任意3d空间坐标,可以做飞行。
2.op的组合应用的核心效果
让一个人悬空到某个特定坐标位置即另一个人的骨骼坐标相对位置,悬空在骑砍mod开发过程中与飞行一样,大多数处理方法都是动作或场景物碰撞体托,其中要做自由的效果以场景物碰撞体为主,但是这种辅助人体悬空采用碰撞体托的方式会带来一系列问题和矛盾以及控制上的不便需要修正,高角度俯冲的掉落或强制位置与放松自由行动的行为矛盾,以及对摔伤伤害的触发(踩着碰撞体运动发生惯性的剧烈晃动会认定你在高速急促落地而受伤),所以agent_set_no_dynamics设参数1便是为了去掉人agent系统固有的惯性和运动位移,由代码操作接管改写控制。这个在处理人涉及到的一切需要在3d空间与另一个运动对象伴随从动运动具有极大的方便,比如固定驾驶员与飞行器的相对位置等等。
3.agent_get_bone_position对于非1倍骨骼(即skl_human值不为1.00000)的skin.py定义的人类种族的扩展使用。
假设skl_human值为1.00000的人
agent_get_position为pos1,对应坐标x1,y1,z1
agent_get_bone_position某特点编号<bone_no>骨骼的坐标为pos2,对应坐标x2,y2,z2
再假设skl_human值为c的人
agent_get_position为pos1,对应坐标x1,y1,z1(骨骼倍数不影响处于同一个动作同帧的人体位置pos)
因为骨骼不是1倍基础大小而不能由agent_get_bone_position获取的某特点编号<bone_no>骨骼的实际坐标为pos3,对应坐标x3,y3,z3
那么(x3-x1)=c(x2-x1)
(y3-y1)=c(y2-y1)
(z3-z1)=c(z2-z1)
在两个不同倍数骨骼的人物做同一个动作同帧状态下,这三个等式恒成立。也就是骨骼放大c倍,任意骨骼坐标的xyz值与人体本身坐标xyz值的差值也扩大c倍。
接下来是代码实例讲解:从我的魔兽争霸mod里https://bbs.mountblade.com.cn/thread-2094063-1-1.html节选:
首先去skin.PY新建一个种族,骨骼大小skl_human为6.00000,这个方法自行搜索解决不多说。然后到troop.PY新建一个以这种种族的新troop命名为trp_susanoo
以下代码加到module_mission_templates需要的场景里,比如lead charge 野战,或者直接全部脚本化后每个场景触发器都引用一遍:条件推荐0.0000,0.0000,0.0000这种,因为伴随从动的运动控制需要高频克服单线程延迟。
(set_fixed_point_multiplier, 1000),
(get_player_agent_no, ":var_0"), #玩家命名id为:var_0
(agent_get_position, pos32, ":var_0"),
(agent_get_team, ":var_2", ":var_0"),
(scene_prop_get_num_instances, ":var_7", "spr_nnksk0"),
(try_begin), #代码块0:以下主要为了刷一些技能特效物体,和本功能没啥关系,也就是上图佐助手上的闪电动画,也是我以后要讲的顶点动画的制作和多样控制再说,主要对本功能有用的涉及(troop_set_slot, "trp_susanoo", 733, 0),初始化一个下面要用的变量,可以在其他地方其他方式考虑初始化一下。
(eq, ":var_7", 0),
(troop_set_slot, "trp_susanoo", 733, 0),
(copy_position, pos33, pos32),
(position_move_y, pos33, -10000),
(position_move_z, pos33, -10000),
(set_spawn_position, pos33),
(spawn_scene_prop, "spr_nnksk0"),
(scene_prop_set_slot, reg0, 200, -1),
(scene_prop_set_visibility, reg0, 0),
(2612, reg0, 0, 390, 500),
(troop_set_slot, "trp_player", 580, "spr_nnksk0"),
(set_spawn_position, pos30),
(spawn_scene_prop, "spr_nnksk1"),
(scene_prop_set_slot, reg0, 200, -1),
(scene_prop_set_visibility, reg0, 0),
(2612, reg0, 0, 390, 500),
(troop_set_slot, "trp_player", 581, "spr_nnksk1"),
(set_spawn_position, pos30),
(spawn_scene_prop, "spr_nnksk2"),
(scene_prop_set_slot, reg0, 200, -1),
(scene_prop_set_visibility, reg0, 0),
(2612, reg0, 0, 790, 600),
(troop_set_slot, "trp_player", 582, "spr_nnksk2"),
(try_end), #代码块0结束
(agent_get_slot, ":var_18", ":var_0", 505), #获取序号505的玩家agentslot 值,这个是为了控制我的英雄qwer四个技能中的三技能持续效果时间进程,从我魔兽中代码阉割一部分拼写进来;
(try_begin), #代码块1:以下是为了控制三技能持续效果在1-2000变化
(eq, ":var_18", 0),
(eq, "$ccd2", 2), #ccd和307号slot都是我来控制技能触发后的持续效果的,也就是须佐近似的持续时间,这个你们可以按照自己的想法写条件,意思就是因为技能触发条件特定,使得slot505从0变成1,一旦slot505大于等于1,则自动从1依此加1到2000如果没其他打断设定,然后2000时归0,形成本技能的持续时间
(agent_slot_eq, ":var_0", 307, 0),
(agent_set_slot, ":var_0", 505, 1),
(else_try),
(is_between, ":var_18", 1, 2000),
(val_add, ":var_18", 1),
(try_begin),
(eq, ":var_18", 3),
(str_store_troop_name, s15, ":var_4"),
(str_store_item_name, s16, "itm_wardenskill2"),
(display_message, "str_showskill"),
(try_end),
(agent_set_slot, ":var_3", 505, ":var_18"),
(else_try),
(eq, ":var_18", 2000),
(agent_set_slot, ":var_0", 505, 0),
(str_store_troop_name, s15, ":var_4"),
(str_store_item_name, s16, "itm_wardenskill2"),
(display_message, "str_diskill"),
(try_end), #代码块1结束
(try_begin), #代码块2:以下是为了控制三技能持续效果在50时按了左手边的alt键使得快速到达持续时间终止(50变1998,到2000结束效果)
(agent_slot_ge, ":var_0", 505, 50),
(key_clicked, key_left_alt),
(agent_set_slot, ":var_0", 505, 1998), #代码块2结束
(try_end),
(try_begin), #代码块3:以下是为了控制三技能持续效果时间区间在启动瞬间和结束瞬间清除之前刷出的旧的的须佐能乎agent
(this_or_next|agent_slot_eq, ":var_0", 505, 1999),
(agent_slot_eq, ":var_0", 505, 3),
(agent_set_no_dynamics, ":var_0", 0), #玩家恢复默认的物理状态,可受重力作用,与下面改变量呼应
(try_for_agents, ":var_27"),
(agent_is_alive, ":var_27"),
(agent_is_human, ":var_27"),
(agent_get_troop_id, ":var_28", ":var_27"),
(eq, ":var_28", "trp_susanoo"),
(troop_get_slot, ":var_29", "trp_susanoo", 733), #"trp_susanoo"的733slot代表本次战斗已经刷了几个susanoo(须佐)这种agent,agent的315表示刷须佐兵时依次编号,如果重置本技能时判断须佐的当前编号小于已刷的总数则fade消失
(neg|agent_slot_ge, ":var_27", 315, ":var_29"),
(agent_set_visibility, ":var_27", 0), #set_visibility为了让人影身,fade为了让人渐隐后消失死亡,为什么要两个一起用,因为我目的要让单位非杀死性死亡消失但是希望立即消失而不是慢慢渐隐
(agent_fade_out, ":var_27"),
(try_end), #代码块3结束
(else_try),
(agent_slot_eq, ":var_0", 505, 5), #代码块3:以下是为了控制刷出须佐能乎这种兵种的agent
(set_show_messages, 0),
(copy_position, pos35, pos32),
(set_spawn_position, pos35),
(spawn_agent, "trp_susanoo"),
(troop_get_slot, ":var_30", "trp_susanoo", 733), #上面说了,"trp_susanoo"的733slot代表本次战斗已经刷了几个susanoo(须佐)这种agent,agent的315表示刷须佐兵时依次编号,用来控制每次释放技能只保证控制最新召唤的,让旧的召唤须佐消失
(agent_set_slot, reg0, 315, ":var_30"),
(store_add, ":var_31", ":var_30", 1),
(troop_set_slot, "trp_susanoo", 733, ":var_31"),
(agent_set_team, reg0, ":var_2"),
(agent_set_no_death_knock_down_only, reg0, 1), #这个避免须佐agent被人打死
(agent_set_speed_modifier, reg0, 250), #须佐agent最好提速,因为人体放大过多,看起来正常移速走的很慢
(agent_set_damage_modifier, reg0, 250),
(else_try),
(agent_slot_ge, ":var_0", 505, 6), (set_show_messages, 1),
(agent_set_no_dynamics, ":var_0", 1), #玩家取消默认的物理状态,不受重力作用,坐标set position可在任意3d空间悬空放置身体
(try_for_agents, ":var_32"),
(agent_is_human, ":var_32"),
(agent_is_alive, ":var_32"),
(agent_get_troop_id, ":var_33", ":var_32"),
(eq, ":var_33", "trp_susanoo"),
(agent_get_team, ":var_12", ":var_32"),
(eq, ":var_12", ":var_2"),
(agent_get_bone_position, 41, ":var_32", 9, 1), #编号9为头部骨骼,即循环所有troop名称为须佐的agent :var_32这个人的头部骨骼坐标获取在骨骼大小为1.000时设为pos41,但注意实际须佐人为6倍骨骼大小
(agent_get_position, pos42, ":var_32"), #循环agent :var_32当前人体位置坐标获取为pos42
(position_get_x, ":var_34", pos42),
(position_get_y, ":var_35", pos42),
(position_get_z, ":var_36", pos42),
(position_get_x, ":var_37", pos41),
(position_get_y, ":var_38", pos41),
(position_get_z, ":var_39", pos41),
(store_sub, ":var_40", ":var_37", ":var_34"), #用pos41,42的xyz坐标作差
(store_sub, ":var_41", ":var_38", ":var_35"),
(store_sub, ":var_42", ":var_39", ":var_36"),
(val_mul, ":var_40", 6), #以上xyz差值乘6即为须佐人susanoo实际的头部骨骼坐标与位置坐标的xyz各差值
(val_mul, ":var_41", 6),
(val_mul, ":var_42", 6),
(store_add, ":var_43", ":var_40", ":var_34"), #人体位置xyz坐标加上实际的须佐人susanoo实际的头部骨骼坐标与位置坐标的xyz各差值得到 6倍骨骼大小的人体头部骨骼坐标xyz值
(store_add, ":var_44", ":var_41", ":var_35"),
(store_add, ":var_45", ":var_42", ":var_36"),
(position_set_x, pos43, ":var_43"),
(position_set_y, pos43, ":var_44"),
(position_set_z, pos43, ":var_45"), #将实际6倍骨骼大小的人种须佐susanoo的头部骨骼坐标设为pos43
(agent_get_look_position, pos36, ":var_0"), #得到玩家自己的视角坐标pos36
(position_copy_rotation, pos43, pos36), #将pos43角度复制pos36的使得pos43坐标的朝向可以根据玩家视角改变
(agent_set_position, ":var_0", pos43), #将玩家设置在可以根据玩家视角变化转角的须佐susanoo的头部位置上。
(try_end),
(try_end),
当然以上的坐标运算可以用相对坐标,但是写成全局比较适合大家理解思路
本实例完毕。
视频效果
一些配套代码涉及的东西比较广就不写了,比如我这个上面最终效果其实是不能控制须佐,而是须佐带着玩家跑,玩家只能站须佐巨人的头上用远程武器或技能高空打击地面上的人。但是对于我的魔兽争霸mod是可以控制的,因为我有RTs魔兽框选控制小兵机制,可以鼠标选中自己的单位鼠标点击移动,所以所有的功能需要一系列配套设施才能合理并完美,单独的展示只是试验和思路示意。
后面还将介绍下其他用法,比如这个是从动人整体位置从动与主动人的某个身体位置,还可以从动人某个身体骨骼位置从动与主动人的某个身体位置,这样可以360度过肩摔等等
|