MC深坑不回头




最近在做个1.12就开始但到现在还没做完的地图...想将命令改成1.15版本以上,顺便问问能否简化和改进:






1. 原本每个NPC生成时有设定不同的几个变数A所在地区,变数B存在时段,标签C盔甲座动作类型,标签D盔甲座移动类型,标签E盔甲座外观类型。


变数所在地区是为了当玩家不在该地区时删除NPC用的。变数存在时段是因为部分地区有早晚不同NPC配置设定。




生成时大概像下面这样:


(最初背景侦测玩家地区及时间,变更玩家的变数)高频侦测当玩家接近了村子A,且时间是早晨时会生成有特定标签NPC。
  1. execute @p [score_变数A_min = 4,score_变数A = 4,score_变数B_min = 2,score_变数B = 2]???summon minecraft:armor_stand 500 16 600 {CustomName:“§5TEST”,Tags:[“村子A”,“早晨”,“举手”,“左右转头”,“男性牧羊人"],CustomNameVisible:1b,Invisible:0b,Invulnerable:1b,NoGravity:0b,NoBasePlate:1b,ShowArms:1b,Small:0b,ArmorItems:[{id:"minecraft:leather_boots",Count:1b,tag:{display:{color:8410132}}},{id:"minecraft:leather_leggings",Count:1b,tag:{display:{color:4806628}}},{id:"minecraft:chainmail_chestplate",Count:1b},{id:"minecraft:skull",Damage:3,tag:{SkullOwner:{Id:"0a101e0e-e4f6-40cf-9228-a0965cf1dda6",Properties:{textures:[{Value:"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOWYxNDVhYmRiNzNlYWFmNGNhNDMyMTkxMWE4NzZlNDMzYTIxOTNkY2NmZGRkMTA5MjFiZjlkNjQ4ZTdhNjMifX19"}]}}}}],HandItems:[{id:"minecraft:stone_pickaxe",Count:1b,Damage:0b},{id:"minecraft:apple",Count:1b,Damage:0b}],DisabledSlots:2039583,Rotation:[45f],Pose:{RightArm:[210f,96f,15f]}}
复制代码


高频侦测当玩家在该NPC附近10格时给予其动作:
entitydata @e[type=armor_stand,r=10,tag=D左右转头] {Pose:{Body:[0f,0f,0f],Head:[3f,35f,0f],LeftLeg:[0f,0f,0f],RightLeg:[0f,0f,0f],LeftArm:[0f,0f,0f],RightArm:[0f,0f,0f]}}


背景计时一小段后:
entitydata @e[type=armor_stand,r=10,tag=D左右转头] {Pose:{Body:[0f,0f,0f],Head:[3f,325f,0f],LeftLeg:[ 0f,0f,0f],RightLeg:[0f,0f,0f],LeftArm:[0f,0f,0f],RightArm:[0f,0f,0f]}}
不同动作的NPC计时长短不同(好像会很耗资源...)
地区大约有五六个,每个地区有十到二十多个NPC...


以上为目前1.12版本,请问是否有较为简易的方式可简化? 做成1.15版+怎么改?




2. 1.15版若要加做玩家在接近到该NPC两格内时,该NPC停止动作并看向玩家(同时有两个以上NPC在玩家附近就一起看向玩家)的效果,该怎么写命令呢? 玩家远离到两格外后重新开始其动作或姿势。





3. 1.15版随机对话系统。比方说我在1.12用的笨方法是检测玩家站到NPC旁时,发射器射出不同名的物品,高频检测到哪个物品名就给该物品对应的标签,再高频侦测标签,依照当时玩家的[变数A所在地区、变数B目前时段、变数甲所在章节、标签乙有无达成条件、标签丙其他...等等],显示不同的对话并删除物品重设发射器。
是否有较简易的命令写法?   若要对话一出现的机率比对话二、对话三等等还要低些(比如十次对话里面对话一只出现一次),具体要怎么写呢?




ˋ4. 这些大部分写在function内,若以datapack架构写,可以怎么连结各个function,将来要找寻或更改时较简便呢?






⊙u⊙
命令方块转移到数据包几乎是必须的,那么我整理一下...
所在地区 - 以分数形式记录于玩家,以标签形式记录于实体
存在时段 - 以标签形式记录于NPC,function定义标签的时长
动作类型 - 动作类型记录于实体标签,动作数据记录于function,持续时间定义于function
移动类型 -
外观类型 -


#通用类型,比如时间,建议记录于假名
#scoreboard objectives add global dummy
#记录玩家所在地区,和用于对比的前一刻地区
#scoreboard objectives add region dummy
#scoreboard objectives add region_pre dummy
#用于记录在同一地区的逗留时间
#scoreboard objectives add region_time dummy

#时间,不知道你要怎么分,我就以游戏里一天的周期写一个
scoreboard players add $time global 1
execute if score $time global matches 24001.. run scoreboard players set $time global 1

#设置地区分数,地区逗留时间
#
#运行于tick函数的开头
#<你标记玩家地区的命令执行于此位置,成就貌似也可以,这里先以命令为例>
#例子 <scoreboard players set @a[x=1,y=0,z=10,dx=20,dy=5,dz=20] region 1>
#例子 <execute as @a at @s if block ~ 0 ~ white_wool run scoreboard players set @s region 1>
execute as @a unless score @s region = @s region_pre run scoreboard players set @s region_time 0
execute as @a if score @s region = @s region_pre run scoreboard players add @s region_time 1
#
#运行于tick函数的末尾
execute as @a run scoreboard players operation @s region_pre = @s region



#当玩家在对应地区时
execute as @a if score @s region matches 1 run function foo:region/1
execute as @a if score @s region matches 2 run function foo:region/2
#当玩家离开地区1时
execute as @a if score @s region_pre matches 1 unless score @s region matches 1 run kill @e[tag=region1]


#foo:region/1
#当玩家进入地区1时
execute if score @s region_time matches 1 run function foo:region/1_setcommon
#生成特定时间的NPC,确保不重复生成
execute if score $time global matches 0..12000 unless entity @e[tag=a] run summon armor_stand ... {Tags:["region1","duration1","a"]}
#移除非特定时间的NPC
execute if score $time global matches 12001..24000 run kill @e[type=armor_stand,tag=duration1]
#
#执行动作
execute at @s run tag @e[distance=..10,tag=common,tag=!exception] add setpose
execute at @s run tag @e[distance=..2,tag=exception,scores={pose_distance=2}] add setpose
scoreboard players add @e[tag=setpose] pose_time 1

#动作设置
execute as @e if score @s pose_time matches 1 run function foo:pose/initial
#设置pose_1的持续时间
execute as @e if score @s pose_time >= @s pose_1 run data remove entity @s Pose
#设置可再次执行动作的时间
execute as @e if score @s pose_time >= @s pose_restart run function foo:pose/count_reset

#foo:pose/count_reset
tag @s remove setpose
scoreboard players reset @s pose_time


#foo:pos/initial
execute if entity @s[tag=a] run data modify entity @s Pose set value {Pose:{Head:[3f,35f,0f]}}
execute if entity @s[tag=common] run data modify entity @s Pose set value {Pose:{Head:[50f,80f,0f]}}



#foo:region/1_setcommon 生成路人NPC
summon armor_stand ... {Tags:["region1","common","pose1"]}
summon armor_stand ... {Tags:["region1","common"]}
summon armor_stand ... {Tags:["region1","common"]}





问题有点多,纯脑补脑子就有点转不过来...
就简单地给之后的问题作下答...还有疑问再补充

2. 在pose设置环节加上apply标签,当玩家靠近时给予附近npc标签apply
有apply标签时停止自身动作,并望向玩家。玩家远离时清楚apply

3. https://www.mcbbs.net/forum.php? ... 18&pid=17744427

4. 一看便懂的函数命名,养成写备注的良好习惯,否则过几天再看就是天书

(=°ω°)丿
命令升级:
https://spu.spgoding.com/


盔甲架动作:
[1.15+][原版模组|ZB]盔甲架编辑器 Release 1.4.0 帧动画功能更新
https://www.mcbbs.net/thread-965816-1-1.html


数据包编辑:
[1.15-1.16] 大憨批(MCF)—— VSCode 数据包函数编辑插件
https://www.mcbbs.net/thread-926724-1-1.html


随机数:
【原版模组】战利品表随机数 —— 更简洁的数据包随机数前置
https://www.mcbbs.net/thread-900914-1-1.html


或者使用战利品表,设置相应的权重。
或者使用断言的 random_chance。
【CBL|SPG】[1.16] 战利品表 —— 数据包的组成文件之一
https://www.mcbbs.net/thread-831542-1-1.html
【CBL|SPG】[1.16] 断言 —— 数据包的组成文件之一
https://www.mcbbs.net/thread-914817-1-1.html


两个命令教程(或者可以翻 Wiki):
【教程】[1.15] execute 命令入门教程
https://www.mcbbs.net/thread-989501-1-1.html
【教程】[1.15] 常见的数据操作方法:入门教程
https://www.mcbbs.net/thread-993805-1-1.html


没有人比我更懂发链接
【1.13-1.15】原版模组入门教程
https://www.mcbbs.net/thread-784662-1-1.html

SL0ANE
本帖最后由 SL0ANE 于 2020-5-31 10:06 编辑

额 这个原版实现有点难呢
可以试下用NPCmod
https://www.mcbbs.net/thread-936521-1-1.html


浅色搞你家
用NPc的mod可以吗?

749112221a
建议直接换npc mod直接做单人地图 别人下载直接下载npcmod 就行了 。

MC深坑不回头
感谢建议使用mod的"回答",我想目前我列出的功能是能在原版中做出来的,如可以的话不想用到任何mod。

⊙u⊙
只能说是编写逻辑。因为你提到地图内有很多npc用到类似或重复的功能,所以写一个系统之后就会很方便

第二个的话像之前说到的那样,在修改Pose的时候加一个标签判断,没有标签代表玩家不在2格内-继续执行动作,有标签则停止当前动作-望向玩家当然,也并不一定得用tag...要求至少是有一个定义npc停止动作的标记


创建板记录和计算玩家与实体之间的距离,使用三维坐标形式,我们只需要对比值的大小,可以省一个开根的过程。
记分板最大值是2147483648,3*(x^2)=2147483648,x=26754
但是为确保精确度,这里的x以0.1格为单位,因此命令获取坐标值时由于记分板不存小数,获取时得乘10,3*(x0^2)=2147483648,x=2675,得出可计算的间距为2675格,够用了。
*因此后面提到npc的distance_pose和distance_stare最好也以0.1格为单位定义
*听说可以利用1.16的attritube做小数点储存和计算,有兴趣的可以用那个方法
#计算执行者和标签实体A的间距
#scoreboard objectives add temp dummy
#scoreboard objectives add distance dummy

#每次运行函数获取间距前给需要计算的实体一个标签A,执行者执行函数
#npc将会是执行者,检测到玩家的距离,玩家获得标签A
#tag @a add A
#execute as @e[tag=npc] run function foo:getdistance
##execute as @e[tag=npc] at @s if entity @a[tag=A,distance=..30] run function foo:getdistance 如果全局最大距离为30可以改用这条


#foo:getdistance
execute store result score $exe_pos0 temp run data get entity @s Pos[0] 10
execute store result score $exe_pos1 temp run data get entity @s Pos[1] 10
execute store result score $exe_pos2 temp run data get entity @s Pos[2] 10
execute store result score $a_pos0 temp run data get entity @e[tag=A,limit=1,sort=nearest] Pos[0] 10
execute store result score $a_pos1 temp run data get entity @e[tag=A,limit=1,sort=nearest] Pos[1] 10
execute store result score $a_pos2 temp run data get entity @e[tag=A,limit=1,sort=nearest] Pos[2] 10

scoreboard players operation $dlt_x temp = $exe_pos0 temp
scoreboard players operation $dlt_y temp = $exe_pos1 temp
scoreboard players operation $dlt_z temp = $exe_pos2 temp
scoreboard players operation $dlt_x temp -= $A_pos0 temp
scoreboard players operation $dlt_y temp -= $A_pos1 temp
scoreboard players operation $dlt_z temp -= $A_pos2 temp
scoreboard players operation $dlt_x temp *= $dlt_x temp
scoreboard players operation $dlt_y temp *= $dlt_y temp
scoreboard players operation $dlt_z temp *= $dlt_z temp

scoreboard players operation $distance temp = $dlt_x temp
scoreboard players operation $distance temp += $dlt_y temp
scoreboard players operation $distance temp += $dlt_z temp
scoreboard players operation @s distance = $distance temp


创建板定义执行动作的距离和停止动作望向玩家的距离,做到不同npc有不同的距离
#执行前需要给npc设置一次执行动作的距离distance_pose,和停止动作望向玩家的距离distance_stare
execute as @e[tag=npc] if score @s distance <= @s distance_pose if score @s distance > @s distance_stare run function <执行动作的函数>

execute as @e[tag=npc] if score @s distance <= @s distance_stare at @s run function foo:npc_facing


#foo:npc_facing
#望向能想到很多种,转头(改Pose Head),转身(改Rotation),转身+转头。这里就以(360*)转头为例...
#水平转向应该是Rotation[0],除非我记错了
summon area_effect_cloud ~ ~ ~ {Tags:["B"]}
execute as @e[type=area_effect_cloud,tag=B,distance=..0.1] at @s facing entity @p eyes run teleport @s ~ ~ ~ ~ ~
execute store result entity @s Pose.Head[1] float 0.01 run data get entity @e[tag=B,limit=1,sort=nearest] Rotation[0] 100
kill @e[type=area_effect_cloud,tag=B,distance=..0.1]