骑马与砍杀中文站论坛

 找回密码
 注册(Register!)

QQ登录

只需一步,快速开始

搜索
购买CDKEY 小黑盒加速器
查看: 1876|回复: 7

[经验与教程] [狂人的知识]较为精细的特技攻击判定方法

[复制链接]

32

主题

72

回帖

181

积分

见习骑士

Rank: 3

UID
3199602
第纳尔
1041
精华
0
互助
30
荣誉
0
贡献
1
魅力
203
注册时间
2020-9-4
鲜花(54) 鸡蛋(0)
发表于 2023-4-24 03:40:02 | 显示全部楼层 |阅读模式
本帖最后由 奥杜因阿卡托什 于 2023-5-5 16:46 编辑

      在写正文之前,先感谢@vegetto提供的灵感和指导,以及@huagao@黑暗路西法@战争傀儡阿格兰@无面的无名氏@阳光肥宅提供的协助,我能这么快把这个内容搓出来离不开和他们的集思广益。下面开始正文。
      首先解释一下我做的是什么东西,简而言之,就是物理特技攻击的判定。这里的特技指的是按特殊键施放的主动技能,包括绝大多数人都用过的ctrl H、ctrl F4这种某种程度上也属于此列,我希望这些主动技能像魂系列的战技释放一样,做出一个特殊动作,对周围的敌人造成伤害。类似下图
      但问题也随之而来,就是攻击的判定。众所周知,用set animation使出来的动作,只有其形,碰撞、伤害、触发那是一律没有,打在人身上直接穿体而过,什么事都没有,而攻击碰撞这块又是死代码中的死代码,几乎没有任何接口,所以想要让这些自己手搓的技能造成伤害,就必须自己写判定,判定命中后再用deliver damage等代码造成伤害。
      事实上,可用的判定方法有很多。这段时间我搜集了不少,比如:
      一、最简单的,触发动作的瞬间获取敌人和玩家的距离,小于多少就扣血。这种方式虽然是最简单,但作为法术来说或许算是合理,用在物攻上可就很难顶了。往前挥了一剑,结果周围一圈的人都死光了,别扭得不行。割草玩家或许会很喜欢这种技能,但要是NPC也能用,玩家成为被割草得一方时,恐怕没人能笑得出来了。
      二、获取武器尖端的位置,当尖端的点与敌人相距小于一定值,就算击中。经过测试,这个方法在用单手剑时还行,但在挥舞长杆时,杆部将成为一个不可忽视的组份,导致很多紧贴攻击者的敌人直接穿杆而过,因为武器尖端在它背后一段距离,所以毫发无损。对于这点,黑暗路西法提供了一个改进思路:在武器上再多锁定几个点,靠这些点覆盖整个杆部,这个方案我还没试,有志之士可以试一下。

      三、由老彼得友情提供的办法,获取武器尖端的位置,沿着武器杆往回射透明箭。这是个很聪明的办法,因为add_missile是一整个封装好的攻击,是玩家能使用的少数自带判定的攻击。这个思路让我想起了《刀剑神域》黑色子弹篇最后打死枪时,桐人手枪连续射击,用接连不断的子弹组成了第二把剑,和这个思路不谋而合:只要武器掠过敌人,武器上连续不断的透明剑就会捅它的腚眼子。但这个方案的问题也有,比如没有击中敌人的箭可能会飞到另一边去击中别人,会击中友军,本来应该对两个人造成伤害结果其中一人吃了两箭等等,对add_missile的参数设置精度要求比较高,需要大量的调试,这个我就没做了。
      四、我自己寻思出的一种方法,只要敌人和武器尖端在同一个方向,而且距离小于武器长度,就算它被攻击到了。把攻击者周围的空间分成26个区域(分法参考三阶魔方,攻击者相当于最中间的机括),然后根据武器尖端和敌人与攻击者的相对位置判断它们在哪个区域,如果在同一区域,就算距离,在攻击范围内就判中。这个办法计算量虽大,但也算是兼顾了方向和距离,因为agent也是有体积的,基本上不是太特殊的动作都能勉强判定。最后我没用这个办法是因为找到了尚未替代,接下来放代码。
      (补充一点,所有方法都有一件要注意的事,敌人的position获取之后并不是在头部或者胸口腹部等,而是在左脚底,这个从openbrf里也能看出来。所以计算是最好把它的坐标往z轴正方向移个1米,移动到差不多腹部的位置。)
      我现在的办法是在四的基础上,改进了运算方法,本来是26分区的,现在直接用三维坐标系的向量点乘算出了武器和敌人的夹角。代码如下:
  1. #input pos1, pos2 and origin position pos3.
  2. #pos1 is weapon end point, pos2 is enemy position, pos3 is player position
  3. #If pos1 and pos2 are in same direction of pos1 and long enough, return reg1 = 1, means can hit.
  4.   ("cf_position_check_hit", [
  5.       (set_fixed_point_multiplier, 100),
  6.       (assign, reg1, 0),
  7.       (get_distance_between_positions, ":cur_distance_1", pos1, pos3),#weapon attack range
  8.       (get_distance_between_positions, ":cur_distance_2", pos2, pos3),#enemy distance
  9.       (ge, ":cur_distance_1", ":cur_distance_2"),#can hit
  10.       (position_get_x, ":cur_x_1", pos1),
  11.       (position_get_y, ":cur_y_1", pos1),
  12.       (position_get_z, ":cur_z_1", pos1),
  13.       (position_get_x, ":cur_x_2", pos2),
  14.       (position_get_y, ":cur_y_2", pos2),
  15.       (position_get_z, ":cur_z_2", pos2),
  16.       (position_get_x, ":cur_x", pos3),
  17.       (position_get_y, ":cur_y", pos3),
  18.       (position_get_z, ":cur_z", pos3),
  19.       (val_sub, ":cur_x_1", ":cur_x"),
  20.       (val_sub, ":cur_y_1", ":cur_y"),
  21.       (val_sub, ":cur_z_1", ":cur_z"),
  22.       (val_sub, ":cur_x_2", ":cur_x"),
  23.       (val_sub, ":cur_y_2", ":cur_y"),
  24.       (val_sub, ":cur_z_2", ":cur_z"),
  25.       (val_mul, ":cur_x_1", ":cur_x_2"),
  26.       (val_mul, ":cur_y_1", ":cur_y_2"),
  27.       (val_mul, ":cur_z_1", ":cur_z_2"),
  28.       (store_add, ":numerator", ":cur_x_1", ":cur_y_1"),
  29.       (val_add, ":numerator", ":cur_z_1"),#x1*x2+y1*y2+z1*z2, dot product
  30.       (store_mul, ":denominator", ":cur_distance_1", ":cur_distance_2"),#magnitude1*magnitude2
  31. #cos=numerator/denominator. When cos equals 1, these two are in the same direction. We need their included angle to be within plus or minus 60 degrees, that is, cos must be greater than 0.5
  32. #But considering that Python may not be able to obtain 0.5, let's transform the inequality and apply 0.5 to the denominator side, that is, the numerator must be greater than the denominator multiplied by 0.5
  33. #      (val_mul, ":denominator", 9),
  34.       (val_div, ":denominator", 2),
  35.       (ge, ":numerator", ":denominator"),
  36.       (assign, reg1, 1),#can hit
  37.     ]),
复制代码
里面写的注释可以看一下,因为怕打不中,我给这个夹角扩大到60度了,其实要更精确的话,改小一点也可以。

      有了这个工具,就可以进行判定了,判定区块的代码如下:
  1. #use for weapon hit check
  2.   ("AoM_weapon_hit_check", [
  3.       (agent_get_bone_position, pos1, "$mission_player_agent", 19, 1),#item.R
  4.       (agent_get_wielded_item, ":weapon_no", "$mission_player_agent", 0),
  5.       (item_get_weapon_length, ":weapon_length", ":weapon_no"),#cm
  6.       (try_begin),
  7.          (item_get_type, ":type_no", ":weapon_no"),
  8.          (eq, ":type_no", itp_type_polearm),
  9.          (val_sub, ":weapon_length", 70),#0.7meter
  10.       (try_end),
  11.       (position_move_y, pos1, ":weapon_length"),
  12.       (try_for_range, ":count_no", 0, 10),
  13.          (store_add, ":slot_no", ":count_no", slot_agent_surrounded_enemy_1),
  14.          (agent_get_slot, ":beattacked_agent_no", "$mission_player_agent", ":slot_no"),
  15.          (gt, ":beattacked_agent_no", 0),
  16.          (agent_is_alive,  ":beattacked_agent_no"),
  17.          (agent_get_position, pos2, ":beattacked_agent_no"),
  18.          (position_move_z, pos2, 100),
  19.          (call_script, "script_cf_position_check_hit"),
  20.          (eq, reg1, 1),
  21.          (agent_deliver_damage_to_agent, "$mission_player_agent", ":beattacked_agent_no"),
  22.       (try_end),
  23.     ]),
复制代码

      有了判定区块还不算完,何时使用、对谁使用也有说法。不可能说没用一次就给全部agents循一次,要知道鉴于一个动作往往有多端伤害,这个东西可能每0.1秒就要触发一次,全部try for agents肯定是吃不消的。因此注意到上面代码中使用的slot_agent_surrounded_enemy_1一系列slot,在按下按键,开始动作时,会先循环一遍agent,把玩家攻击范围(可能会稍大一点)里的敌人选出来,储存在这些槽里,考虑到这种物理攻击应该不会打中太多人,就只留了10个槽。后续判定就只要从这十人里循环,压力大大减轻。
      作为示范,我以一个高位回旋斩的动作作为示范。这个动作时在战风新纪元里找看到的,叫parry_attack_slashleft_onehanded_y,可以去看一看,基本是往左引一段距离蓄力,然后转体360度斩一圈。之前听无名氏说战风新是开源的,就用了,但在此还是不放出来了。我给这个动作设置的时间是1.6秒。因为在我的mod里,角色界面可以选择技能,所以给它套了一个item,储存技能图案、名字、介绍和一些数据。item是这样的
  1. ["active_sweep_away", "active_sweep_away", [("active_sweep_away", 0)], itp_unique, 0, <b>15131007</b>, weight(4)|abundance(10)|max_ammo(0)|food_quality(0), imodbits_none],
复制代码
item之中,金钱部分的15131007,其实表示该技能的四个判定点,分别是发动后的0.4秒、0.5秒(第一次前挥阶段)、0.7秒、0.9秒(转过360度后第二次前挥),怎么得出来的之后再说。
      这一系列的东西最终指向肯定是mission trigger,这里会提供两个,第一个是计时器,第二个是工作的主要部分。
  1.       (0, 0, 0.1, [],
  2.       [
  3.             (try_for_agents, ":agent_no"),
  4.                (agent_is_alive,  ":agent_no"),
  5.                (agent_is_human,  ":agent_no"),
  6.                (agent_get_slot, ":timer_count", ":agent_no", slot_agent_skill_timer),
  7.                (gt, ":timer_count", 0),
  8.                (val_sub, ":timer_count", 1),
  9.                (agent_set_slot, ":agent_no", slot_agent_skill_timer, ":timer_count"),
  10.             (try_end),
  11.          ]),
复制代码
计时器的原理是利用触发时间的最后一项制造0.1秒的延迟,然后每0.1秒所有的slot_agent_skill_timer都减少一。
      然后是主要工作部分
  1.       (0, 0, 0, [],
  2.       [
  3.       (try_begin),                          #activation part, only working at the point when the button is clicked or ai gets permission.
  4.          (is_between, ":active_skill_no", "itm_active_skills_begin", "itm_active_skills_end"),         
  5.          (item_get_slot, ":attacker_anim_no", ":active_skill_no", slot_active_skill_attacker_anim),
  6.          (gt, ":attacker_anim_no", 0),
  7.          (set_fixed_point_multiplier, 100),

  8.          (agent_get_position, pos1, ":attacker_agent_no"),
  9.          (agent_get_team, "$mission_player_team",  ":attacker_agent_no"),
  10.          (agent_get_wielded_item, ":weapon_no", ":attacker_agent_no", 0),
  11.          (item_get_weapon_length, ":weapon_length", ":weapon_no"),#cm
  12.          (val_add, ":weapon_length", 150),
  13. #         (val_div, ":weapon_length", 100),

  14.          (assign, ":slot_no", slot_agent_surrounded_enemy_1),
  15.          (try_for_agents, ":agent_no"),
  16.             (agent_is_alive,  ":agent_no"),
  17.             (le, ":slot_no", slot_agent_surrounded_enemy_10),#at most ten enemies will be attacked
  18.             (assign, ":continue", 0),
  19.             (try_begin),
  20.                (agent_is_human,  ":agent_no"),
  21.                (agent_get_team, ":team_no",  ":agent_no"),
  22.                (teams_are_enemies, "$mission_player_team", ":team_no"), #agent is hostile
  23.                (assign, ":continue", 1),
  24.             (else_try),
  25.                (neg|agent_is_human,  ":agent_no"),#horse
  26.                (agent_get_rider, ":rider_agent_no", ":agent_no"),
  27.                (le, ":rider_agent_no", 0),#have no rider
  28.                (assign, ":continue", 1),
  29.             (else_try),
  30.                (neg|agent_is_human,  ":agent_no"),#horse
  31.                (gt, ":rider_agent_no", 0),#have rider
  32.                (agent_get_team, ":team_no",  ":rider_agent_no"),
  33.                (teams_are_enemies, "$mission_player_team", ":team_no"), #rider is hostile
  34.                (assign, ":continue", 1),
  35.             (try_end),
  36.             (eq, ":continue", 1),

  37. #            (agent_get_position, pos2, ":agent_no"),
  38. #            (get_distance_between_positions_in_meters, ":cur_distance", pos1, pos2),
  39. #            (le, ":cur_distance", ":weapon_length"),
  40.             (agent_set_slot, ":attacker_agent_no", ":slot_no", ":agent_no"),
  41.             (val_add, ":slot_no", 1),
  42.          (try_end),

  43.          (agent_set_animation, ":attacker_agent_no", ":attacker_anim_no"),
  44.          (item_get_max_ammo, ":max_timer", ":active_skill_no"),
  45.          (agent_set_slot, ":attacker_agent_no", slot_agent_skill_timer, ":max_timer"),
  46.          (agent_set_slot, ":attacker_agent_no", slot_agent_activiting_skill, ":active_skill_no"),
  47.       (try_end),
  48.          ]),
复制代码
第一个try_begin之后的区域,很显然是按数字键1激活该动作,包括我上面说的预选10个敌对agent。第二个区域看起来比较简短,主要是大部分都被塞进script了。可以看到其中设置timer的地方,明明这个动作是1.6秒,却设置了19。剩下这三行首先把时间获取了出来,然后call1了一个上面没出现过的script。这个script就是进行时间判断的部分,也是上面item value15131007表示0.4、0.5、0.7、0.9秒,以及timer从19开始的原因,代码如下
  1. #use for weapon hit check
  2. #input timer and skill id
  3.   ("AoM_active_weapon_hit", [
  4.       (store_script_param_1, ":timer_count"),
  5.       (store_script_param_2, ":active_skill_no"),

  6.       (store_item_value, ":attack_check_point", ":active_skill_no"),
  7.       (agent_get_position, pos3, "$mission_player_agent"),
  8.       (try_begin),
  9.          (store_div, ":attack_point", ":attack_check_point", 100000000),
  10.          (gt, ":attack_point", 0),
  11.          (eq, ":attack_point", ":timer_count"),#can attack
  12. <b>         (val_sub, ":timer_count", 1),
  13.          (agent_set_slot, "$mission_player_agent", slot_agent_skill_timer, ":timer_count"),</b>
  14.          (call_script, "script_AoM_weapon_hit_check"),
  15.       (else_try),
  16.          (val_mod, ":attack_check_point", 100000000),
  17.          (store_div, ":attack_point", ":attack_check_point", 1000000),
  18.          (gt, ":attack_point", 0),
  19.          (eq, ":attack_point", ":timer_count"),#can attack
  20. <b>         (val_sub, ":timer_count", 1),
  21.          (agent_set_slot, "$mission_player_agent", slot_agent_skill_timer, ":timer_count"),</b>
  22.          (call_script, "script_AoM_weapon_hit_check"),
  23.       (else_try),
  24.          (val_mod, ":attack_check_point", 1000000),
  25.          (store_div, ":attack_point", ":attack_check_point", 10000),
  26.          (gt, ":attack_point", 0),
  27.          (eq, ":attack_point", ":timer_count"),#can attack
  28. <b>         (val_sub, ":timer_count", 1),
  29.          (agent_set_slot, "$mission_player_agent", slot_agent_skill_timer, ":timer_count"),</b>
  30.          (call_script, "script_AoM_weapon_hit_check"),
  31.       (else_try),
  32.          (val_mod, ":attack_check_point", 10000),
  33.          (store_div, ":attack_point", ":attack_check_point", 100),
  34.          (gt, ":attack_point", 0),
  35.          (eq, ":attack_point", ":timer_count"),#can attack
  36. <b>         (val_sub, ":timer_count", 1),
  37.          (agent_set_slot, "$mission_player_agent", slot_agent_skill_timer, ":timer_count"),</b>
  38.          (call_script, "script_AoM_weapon_hit_check"),
  39.       (else_try),
  40.          (val_mod, ":attack_check_point", 100),
  41.          (gt, ":attack_point", 0),
  42.          (eq, ":attack_check_point", ":timer_count"),#can attack
  43. <b>         (val_sub, ":timer_count", 1),
  44.          (agent_set_slot, "$mission_player_agent", slot_agent_skill_timer, ":timer_count"),</b>
  45.          (call_script, "script_AoM_weapon_hit_check"),
  46.       (try_end),
  47.     ]),
复制代码
这里面有五个结构基本相同的部分,是数位储存,上面那个15131007每两位表示一个时间,那么应该是1.5、1.3、1.0、0.7这四个点,因为时间是从大往小减的,设总时间为X,那这四个判定时刻分别是X-1.5,X-1.3,X-1和X-0.7——当然实际情况没有这么简单。实操中发现一个问题,为了保证触发的灵敏,工作部分的触发器设置的是(0,0,0,这个反应是比0.1秒还快的,差不多0.015秒左右有一次,这导致在四个判定点本来应该只判定一次的,因为它响应得快,在下一个0.1秒到来前又多判定了六七次,导致攻击也多了六七次,这肯定不行。因此我这里判定结束后直接把timer又减了1,强制它提前进去下一个0.1秒,避免重复判定。这么做的结果就是1.5、1.3、1.0这三个点各快进了0.1秒,实际上对应的时刻应该是X-1.5,X-1.3-0.1,X-1-0.1-0.1和X-0.7-0.1-0.1-0.1,成了0.4秒、0.5秒、0.7秒、0.9秒。整个timer的时间,自然也从1.6秒增加到了1.9秒。很简单的小学数学,细想一下就能明白了。
     最后把新增的common也放出来,自然都是agent部分的
  1. slot_agent_skill_timer      = 80
  2. slot_agent_surrounded_enemy_1      = 81
  3. slot_agent_surrounded_enemy_2      = 82
  4. slot_agent_surrounded_enemy_3      = 83
  5. slot_agent_surrounded_enemy_4      = 84
  6. slot_agent_surrounded_enemy_5      = 85
  7. slot_agent_surrounded_enemy_6      = 86
  8. slot_agent_surrounded_enemy_7      = 87
  9. slot_agent_surrounded_enemy_8      = 88
  10. slot_agent_surrounded_enemy_9      = 89
  11. slot_agent_surrounded_enemy_10      = 90
复制代码
     开始mission前记得把这些都归零。
  1.       (ti_on_agent_spawn, 0, 0, [],
  2.       [
  3.          (store_trigger_param_1, ":agent_no"),
  4.          (assign, ":slot_begin", slot_agent_skill_timer),
  5.          (store_add, ":slot_end", ":slot_begin", 11),
  6.          (try_for_range, ":slot_no", ":slot_begin", ":slot_end"),#slot_agent_surrounded_enemy
  7.             (agent_set_slot, ":agent_no", ":slot_no", -1),
  8.          (try_end),
  9.          ]),
复制代码
     这次没有放出插入即用的傻瓜代码,主要是这个实在是一个特别大的系统,横跨的部分太多,从openbrf里的技能图片到prsnt的新角色界面到anim的新动作到新粒子效果,东西太多。不过上述的代码基本也是完成度95%以上的,只要把anim部分自己完善一下就能用。希望能对正在做类似机制的moder和有志于mod的汽油有所帮助。
      由于中文站传不了太大的视频,所以把视频传干杯站了,链接是https://www.bilibili.com/video/B ... 13c7736d290eab41395,想看效果的可以去看看。
      可以看出这个攻击虽然没有什么额外的伤害加成,但是是多段的,角度对的话,可以让一名敌人吃下最多四次攻击。这就是我整这个的目的,毕竟要是都是获取距离然后直接扣血,那除了远程游斗和肉装站撸外没有别的玩法了,现在这么改进一下,如果敌人对玩家使用这一招,玩家有机会可以躲它的攻击,如果是玩家使用这个技能,也可以通过一定时间的练习,配合走位把尽可能多段的攻击打在同一个人身上。比如这个技能一号判定点是在正前方、三号判定点是在左前方,如果玩家打出第一击后朝前方踏出一步,然后立即左转,可以让某个敌人吃完1、3、4号三次攻击,这个操作我视频里好像也搓出来了一次。后续应该会在训练场或竞技场里新加一个训练室,专门给玩家熟悉这些特技。

5月5日更新
      我发现try for range本身就可以只检测某个位置周围的单位,也就是说不用再遍历所有agent一个一个判断距离了。利用这点优化一下,虽然不知道能提升多少性能,但是蚊子再小也是肉,战团这么一点点算力,能优化多少算多少。

评分

参与人数 1第纳尔 +10 魅力 +1 收起 理由
agjib + 10 + 1 您的帖子很有价值!

查看全部评分

鲜花鸡蛋

杰喵喵  在2024-1-14 17:52  送朵鲜花  并说:我非常同意你的观点,送朵鲜花鼓励一下
huagao  在2023-5-7 21:46  送朵鲜花  并说:直接偷了
黑暗路西法  在2023-4-25 11:12  送朵鲜花  并说:奥杜因的手,好汉

16

主题

586

回帖

326

积分

见习骑士

Rank: 3

UID
3102512
第纳尔
2380
精华
0
互助
29
荣誉
0
贡献
0
魅力
63
注册时间
2019-9-6

骑砍中文站APP会员勋章战团正版勋章霸主正版勋章

鲜花(51) 鸡蛋(0)
发表于 2023-4-24 11:10:36 | 显示全部楼层
你小子果真是生产队最勤奋的
崩!撤!卖!溜!

0

主题

57

回帖

17

积分

随仆

Rank: 1

UID
3346943
第纳尔
519
精华
0
互助
0
荣誉
0
贡献
0
魅力
0
注册时间
2022-4-8
鲜花(2) 鸡蛋(0)
发表于 2023-4-24 12:47:52 来自手机 | 显示全部楼层
这B不要命的太勤奋了来自: Android客户端

202

主题

984

回帖

986

积分

子爵[版主]

Rank: 7Rank: 7Rank: 7

UID
2893127
第纳尔
14521
精华
0
互助
90
荣誉
18
贡献
325
魅力
775
注册时间
2017-11-5
鲜花(428) 鸡蛋(0)
发表于 2023-4-24 13:16:09 来自手机 | 显示全部楼层
本帖最后由 vegetto 于 2023-4-24 15:44 编辑

Agent pos指的是在openbrf直角坐标系里面原点o的位置对应在形体上的点。因为人可以做动作改变自己模型的位置,所以说对应形体上的位置不固定。比如有的人直接在骨骼动作素材里面就把人的位置抬高悬空,所以set pos到地面上的时候人也就是悬空的,系统不会自动检测你的脚把你再往地面上平移。场景物和界面系统也是,就像界面,如果你不设置居中或者是某一个正方形的角点放在坐标系原点上,你使用放大缩小的时候就会产生非线性变化就是因为你的形体中心不在原点上,所以说会有一个几何外形与原点的等效直线距离也被放缩。一个物体的位置不和它的形体有关,和他的原点有关,游戏它又不会自动检测你的整个外形轮廓在哪里然后自动帮你调整到最合适的位置。

比如下面这张图假设是在openbrf里面的情况,分为上下两个示意图,上一个示意图表示一个正方体某一个下面的角点在坐标系的o点上,下一个示意图表示那个角点在坐标系o点比如200高度的距离,假如get pos,那么对于上一个图来说整个物体的程序上的pos就是左下角的正方体角点。但是对下一个图来说物体程序上的pos距离自己正方体形体上的左下角的角点高度相差200。
这就是因为你们经常导入别人的现成装备自己不经常微调模型的坐标。为什么导入brf要调整模型的位置在合适的位置进行平移旋转放缩,就是因为你代码get set操作的对象位置点只是一个表达模型相对位置的brf内坐标系oxyz原点o,而不是想当然的认为他是有形的模型形体上的某个点,所以才会经常出现有些人随便导入装备不注意它的坐标位置,才导致进入游戏之后位置错误或看不到模型,模型的整个形体的相对位置是根据brf里面直角坐标系的原点,获取位置设置位置就是将这个坐标系的原点放到设定pos上,然后比对brf里形体的位置出现在游戏里。假设比如说你在brf内某整个模型能够看到的形体部分就是一个很细小的球点,坐标是(1,1,1),那么在游戏里代码上设置这个物体到(1,1,1)的位置时,实际上你看到的这个有形体的球点整体就跑到了(2,2,2),的游戏场景内位置。场景物人类还有界面系统pos的get set都是这个道理。

所以说不是想当然人的坐标和某个身体部位有关,比如说你的人物在动作上做了悬空,那agentpos还在脚下面好远处,假如你的人做了摔倒动作,那有可能靠近腰。所以人体的打击坐标是要根据骨骼坐标来算更准,但是一般情况大多近似是你这边说的脚下的情况,因为大部分的时候人的动作脚踩原点O并让腰与brf里的z轴大致重合的情况下做一定幅度摆动的。

所以要纠正很多人这种模型方面的误区,你们可以看很多游戏里面的做法,像很多游戏就和骑砍一样,跑步闪避飞行这类的动作只做身体姿势动作,但是不做相关的位移在模型动作查看器里面,就是因为如果你模型有位移,那你整个模型对应的参考对象在播放动作的时候,他的位置pos与我们希望在游戏里表现的形体之间就存在动态的位置改变,这时候如果代码上也需要进行位移控制,那在针对有效的形体达到我们需要的打击和位移目标的时候带来控制上的不便,比如说你在模型查看器里面把你的动作做成步行动作身体第0秒在原点上,每帧往y的方向身体挪10。那到了游戏里面代码控制的时候想要让他原地旋转,就只能按ymove单位pos减帧数时间x10距离把自己的身体先在整个动画播放区间范围内挪到模型oxyz坐标系原点o上,才能进行原地rotate旋转。




20230424131123front1_0_2893127_Fpz1HHel2y0HFsFcqQaHbt1WBLNL.jpg





其实最简单的方式可以这么理解,
你可以把整个openbrf的看不见形体的Oxyz坐标系看做是模型的一部分,看成一个整体,你想要代码在游戏内挪动这个模型的对象,人也好场景物也好界面也好,就是带着整个坐标系,把brf坐标系的原点挪到对应的位置上,进而产生了形体位置的改变。所以说agent pos prop pos overlay pos并不是你的模型它的人脚人腰部位,它的立方体的哪个边角点它的四边形界面的对角线某个点就是他的什么什么位置,而是看模型及其动画在brf里怎么放的






来自: iPhone客户端

30

主题

200

回帖

169

积分

见习骑士

Rank: 3

UID
2462463
第纳尔
1472
精华
0
互助
20
荣誉
0
贡献
0
魅力
50
注册时间
2015-3-3
鲜花(43) 鸡蛋(0)
发表于 2023-4-25 11:11:23 | 显示全部楼层
来看论文了支持支持

4

主题

70

回帖

47

积分

扈从

Rank: 2Rank: 2

UID
3042378
第纳尔
668
精华
0
互助
3
荣誉
1
贡献
0
魅力
0
注册时间
2018-11-17
鲜花(22) 鸡蛋(0)
发表于 2023-4-28 20:47:22 | 显示全部楼层
从boss血条来的,请问我能借用一下你的代码吗

32

主题

72

回帖

181

积分

见习骑士

Rank: 3

UID
3199602
第纳尔
1041
精华
0
互助
30
荣誉
0
贡献
1
魅力
203
注册时间
2020-9-4
鲜花(54) 鸡蛋(0)
 楼主| 发表于 2023-4-28 22:06:16 | 显示全部楼层
战争之疯子 发表于 2023-4-28 20:47
从boss血条来的,请问我能借用一下你的代码吗

我放出来就是为了给moder用的,免得同样的功能被不同人反反复复研究出来十万甚至九万次。

鲜花鸡蛋

战争之疯子  在2023-4-29 07:29  送朵鲜花  并说:只能多谢阁下!

13

主题

85

回帖

144

积分

见习骑士

Rank: 3

UID
3304622
第纳尔
605
精华
0
互助
19
荣誉
2
贡献
0
魅力
123
注册时间
2021-12-28
鲜花(26) 鸡蛋(0)
发表于 2024-1-14 17:53:18 | 显示全部楼层
很好的帖子,感谢分享
您需要登录后才可以回帖 登录 | 注册(Register!)

本版积分规则

Archiver|手机版|小黑屋|骑马与砍杀中文站

GMT+8, 2024-4-30 20:13 , Processed in 0.128851 second(s), 32 queries , Gzip On, MemCached On.

Powered by Discuz! X3.4 Licensed

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表