本帖最后由 tineseack_bk 于 2019-4-28 17:11 编辑
【前排说明】

本贴内研究的内容,是在处于普遍状况的前提下讨论的,即是 FOV=70(Normal)



(游戏中的效果,在视野内的实体被高亮显示)



(这是第三人称下的参考图,左边的村民其实已经超出了第一人称下的视野,因此没有被高亮显示)


    理论上来说,检测玩家的视野内实体是不现实的,因为玩家的视野范围并非确定,其取决于你的显示器长宽比,且会根据 FOV(Field of Vision) 变化。当然,如果我们假定玩家的 FOV=70,且长宽比为普遍的 16:9,这个问题还是有一定的探讨价值的。

    至于精度问题,由于 MC 的记分板只支持整型,所以咱们的运算暂时只能处于低精度程度(这是一个很大的缺点,希望能找到其他方法避免这个问题!)









一、视野

    首先我们要明确视野是什么:

视野是指人的头部和眼球固定不动的情况下,眼睛观看正前方物体时所能看得见的空间范围。

在现实中,人的视野是一个锥面;而在游戏中,则是我们平常看到的整个游戏界面。经过测试得知,玩家的视野范围是一个以眼睛为顶点(该点位于玩家头部模型内)、以有着和屏幕同样的长宽比矩形为底面、底面中心与顶点均处于视平面上的四棱锥

    (图中点 O 为玩家眼睛所在点,也即是稍后会用到的坐标原点;点 H 是该棱锥一个竖直截面上的中心;四边形 ABCD 的四边称为边框)





二、如何判断实体在视野范围内
[法三]





[法二]

    我们首先把问题简化:假设玩家的视野是一个正方形,那么易知选到的范围即是玩家视野前方的 ±45° 正四棱锥区域。
    于是,首先我们可以设置五个基准点,作为判断实体是否在这个范围内的媒介。它们分别位于玩家的正前方、正上方、正下方、正左方和正右方
    其次,我们让每一个目标标记一个最近的基准点;
    那么,如果该实体标记到的基准点是正前方的点,它就会在该区域内。
    而对于 16:9 的屏幕长宽比来说,只需要对这些基准点的位置进行微量偏移即可。

设置基准点
    我们首先初始化五个 AEC,作为五个基准点,并且给它们用分数编号
scoreboard objectives add dfov_selection dummy

# 视野边框
execute at @a run summon area_effect_cloud ~ ~ ~ {CustomName:"\"front\"",Tags:["dfov"],Duration:2100000000}
execute at @a run summon area_effect_cloud ~ ~ ~ {CustomName:"\"left\"",Tags:["dfov"],Duration:2100000000}
execute at @a run summon area_effect_cloud ~ ~ ~ {CustomName:"\"right\"",Tags:["dfov"],Duration:2100000000}
execute at @a run summon area_effect_cloud ~ ~ ~ {CustomName:"\"top\"",Tags:["dfov"],Duration:2100000000}
execute at @a run summon area_effect_cloud ~ ~ ~ {CustomName:"\"bottom\"",Tags:["dfov"],Duration:2100000000}

# 边框分数初始化
scoreboard players set @e[tag=dfov,name=front] dfov_selection 1
scoreboard players set @e[tag=dfov,name=left] dfov_selection 2
scoreboard players set @e[tag=dfov,name=right] dfov_selection 3
scoreboard players set @e[tag=dfov,name=top] dfov_selection 4
scoreboard players set @e[tag=dfov,name=bottom] dfov_selection 5

然后我们得让它们随时跟着玩家,并且有一定的偏移量(本贴中的偏移量数值面向 16:9 的屏幕,且仅为实际测试得出,为近似值,并未进行实际计算):
# 边框跟随玩家
execute as @a at @s anchored eyes run tp @e[name=front,tag=dfov] ^ ^ ^0.01
execute as @a at @s anchored eyes run tp @e[name=left,tag=dfov] ^0.01 ^ ^-0.00268
execute as @a at @s anchored eyes run tp @e[name=right,tag=dfov] ^-0.01 ^ ^-0.00268
execute as @a at @s anchored eyes run tp @e[name=top,tag=dfov] ^ ^0.01 ^0.0028
execute as @a at @s anchored eyes run tp @e[name=bottom,tag=dfov] ^ ^-0.01 ^-0.004

我们再让每个实体标记最近的基准点,所涉及的方法是分数的赋予
# 每个生物选择最近的 dfov mk,若是 front 则加tag
# 这里提及的 tag=mob 指的是所有生物
#初始化
scoreboard players add @e[tag=mob] dfov_selection 0
# 让每个实体将最近的一个基准点的分数(即刚才用来标序号的分数)赋予自己
execute at @a as @e[type=!player,tag=!dfov,tag=mob] at @s run scoreboard players operation @s dfov_selection = @e[tag=dfov,limit=1,sort=nearest] dfov_selection
# 如果分数 = 1,即选到了 front,则加 tag
execute as @e[type=!player,tag=mob] run tag @s remove seen
execute as @e[scores={dfov_selection=1},tag=mob] run tag @s add seen

最后,直接选取 tag=seen 的实体即可。



    目前该方法仍然存在一些缺点:
  • 精度不能完全把控:每一种生物的模型和碰撞箱大小都不同,因此有可能出现实体的边缘部分出现在视野内但未检测到的情况;
  • 当实体与玩家靠得足够近,会产生一些误差(与基准点的偏移量有关)。


[法一](该方法精度较低且复杂化了)

   





三、考虑方块遮挡
    (实现方法参考 @chyx 的 [http://www.mcbbs.net/thread-771638-1-1.html])
    以上的内容只考虑了目标实体在“视野范围内”,而这个范围是没有考虑实体被不透明方块遮挡住的,因此我们需要对此进行修正。
    很容易想到,在每个实体处和玩家的眼睛所在点连一条线,如果这条线上有不透明方块,则判断该实体被遮挡。在实际运用中,我们会发现实体的体积大多数是不相同的,因此我们在判断的时候可能需要考虑多个点,例如选取实体的头与脚两个判断点来连线。然而,如果用 tp 实体的方法来实现,在目标数量很大的情况下,资源占用是很大的;因此我们需要借助函数递归,直接用坐标判断方块。在研究了上面提及的 chyx 的方法后,我进行了少量修改,得出了一个基本适用的模块:
# (function)dfov:fix
# 遮挡修正
execute as @e[tag=mob,tag=seen] at @s positioned ~ ~2 ~ facing entity @p eyes run function dfov:fix_head
execute as @e[tag=mob,tag=seen] at @s positioned ~ ~ ~ facing entity @p eyes run function dfov:fix_feet
tag @e[tag=head_hidden,tag=feet_hidden] remove seen
# (function) dfov:fix_head
# 头部
tag @s remove head_hidden
execute if entity @s[distance=..120] unless block ~ ~ ~ #dfov:air run tag @s add head_hidden
execute if entity @s[distance=..120] unless entity @a[distance=..1] if block ~ ~ ~ #dfov:air positioned ^ ^ ^0.5 run function dfov:fix_head
# (function) dfov:fix_feet
# 脚部
tag @s remove feet_hidden
execute if entity @s[distance=..120] unless block ~ ~ ~ #dfov:air run tag @s add feet_hidden
execute if entity @s[distance=..120] unless entity @a[distance=..1] if block ~ ~ ~ #dfov:air positioned ^ ^ ^0.5 run function dfov:fix_feet

# (tag) dfov:air{
  "replace": false,
  "values": [
    "minecraft:air", "minecraft:water", "minecraft:lava", "minecraft:glass", "#minecraft:fences"
  ]
}

    在这里我分了两个判断点,分别是实体的脚部以及 ~ ~2 ~ 的位置,然后以它们为起点,朝向最近的玩家的眼睛,通过递归 dfov:fix_head 与 dfov:fix_feet 两个函数来分别判断连线上是否有不透明方块。简述一下运作原理:
    拿脚部的判断点来说。首先由该点作为起始点,朝向玩家的眼睛(注意,这个朝向将会在接下来的函数递归中继承),判断该点上是否是不透明方块。若是,则直接输出(加上 feet_hidden 的 tag);若否,则将 ^ ^ ^0.5 的位置作为下一个判断点,在这个点上继续执行本函数。这里的参数 0.5 是可更改的,将会影响一定的速度和精度。至于 if entity @s[distance=..120] 则是限制该判定半径为 120 格。
    两个子函数的输出分别是给目标实体加上两个 tag,以标记它们是否分别被遮挡。当两个标签同时存在时,就表示实体被方块遮挡,移除 seen 标签。

    效果展示:
(这是正常情况,可以看到铁傀儡)
(当铁傀儡的头和脚都被遮挡,就无法被看见)

(当玩家蹲下,看到铁傀儡的脚时,铁傀儡再一次被判断“被看见”)

(在玻璃后的实体也是可以被看见的)




四、优化

   




   

    于 190427 更新法二




[groupid=1349]The Minecraft Lover[/groupid]

detect_fov_ver2.zip

4.91 KB, 下载次数: 38

数据包_0427

回复:

tineseack_bk

本帖最后由 tineseack_bk 于 2019-4-28 17:10 编辑

感谢 @crafter 的数据测试,感谢 @switefaster  感谢 @1041159637 @Ruainbow_ 的技术讨论!

2019-04-20 15:15:00

SMFX阜星

好东西,收藏了
果然1.13+后真的是无所不能,再次大大拔高了CB能力

2019-04-20 15:28:00

tineseack_bk

ruhuasiyu 发表于 2019-4-20 23:28
你可以看下这个 http://www.mcbbs.net/thread-796669-1-1.html

感谢分享。还没有细读,但是感觉能对这个算法优化很多

2019-04-20 15:33:00

heroicsea

太强了。。

2019-04-20 15:33:00

tineseack_bk

本帖最后由 tineseack_bk 于 2019-4-20 23:40 编辑

系统错误,重发了两次回复

2019-04-20 15:34:00

tineseack_bk

本帖最后由 tineseack_bk 于 2019-4-20 23:40 编辑

系统错误,重发了两次回复

2019-04-20 15:34:00

Ruainbow_

tql,wsl.
tql,wsl.

2019-04-20 15:43:00

crafter

后排支持,紫薯布丁

2019-04-20 16:01:00

chyx

execute as @e[tag=front] at @s positioned ~ ~1.62 ~ facing entity @p eyes run function general:fix_head

........

我觉得我在qq里不是这样跟你说的。
顺便 请参考上面ruhuasiyu的评论。
再其次 其实你的这些内容只要5个药水云就好了。

2019-04-20 16:09:00

tineseack_bk

chyx 发表于 2019-4-21 00:09
........

我觉得我在qq里不是这样跟你说的。

抱歉我这个好像是直接复制的测试时候的命令了,因为我是用玩家自己来测试
其次五个 aec 指的是什么?

2019-04-20 16:15:00

muyuxiaofensi

看不懂,你自己看看

2019-04-21 02:23:00

uhuichongfu

同样是mc,玩法大大不同啊。
这样的技术贴,是不是可以考虑申请图章了呢?

2019-04-23 22:58:00

tineseack_bk

buhuichongfu 发表于 2019-4-24 06:58
同样是mc,玩法大大不同啊。
这样的技术贴,是不是可以考虑申请图章了呢? ...

首先谢谢支持

其次这个贴子现在发表的方法精度仍然比较低,我近期会参考上面 chyx 提供的另一个思路优化

2019-04-26 12:02:00

祁轩

虽然看不懂但是还是很想学

2019-04-26 13:46:00

chyx

本帖最后由 chyx 于 2019-4-27 23:57 编辑

看完了法2了。
写的真不好。
因为被您在帖子里提到,我感觉有些丢脸。

2019-04-27 15:56:00

tineseack_bk

标题: b
chyx 发表于 2019-4-27 23:56
看完了法2了。
写的真不好。
因为被您在帖子里提到,我感觉有些丢脸。 ...

对不起,我的表达能力实在有限,也是第一次发表类似的研究贴,而且不像您一样涉猎丰富。首先请海涵这篇贴子里面包含的种种错误或不严谨之处,其次感谢您百忙之中还能来对这个不成熟的贴子评论且指出不足,打扰了真是抱歉。谢谢茄子!

2019-04-28 09:30:00

cf6513272991


同样是mc,玩法大大不同啊

2019-05-25 05:25:00

涩会普云

有点小东西

2019-05-25 09:33:00

cf6513272991

好东西,收藏了谢谢楼主

2019-05-25 23:58:00

kayn-

这个东西找一些怪方便多了

2020-08-15 16:18:00

不忘吃心

感觉有点像透视啊

2020-08-22 06:53:00