Minecraft(我的世界)中文论坛

 找回密码
 注册(register)
查看: 2181|回复: 9

[命令] [CBL|K_bai] 用命令写一个碰撞物理引擎 [18w03b]

[复制链接]
kongbaiyo 当前离线
帖子
主题
精华
贡献
最后登录
1970-1-1
爱心
积分
5207
钻石
性别
保密
注册时间
2014-3-14
查看详细资料
发表于 2018-1-26 20:44:11 | 显示全部楼层 |阅读模式

您尚未登录,立即登录享受更好的浏览体验!

您需要 登录 才可以下载或查看,没有帐号?注册(register)

x
本帖最后由 kongbaiyo 于 2018-1-26 20:47 编辑

用命令写一个碰撞物理引擎

1.13的预览版可以通过记分板的数据修改实体的NBT

我立刻想到了可以由此制作一个任意实体的碰撞物理引擎~
此处的都要用 NoGravity:1 屏蔽原版的重力 (甚至让Motion时刻为零 不过没有必要)效果图:

附件的datapack就是这个效果!
先执行function bounce:_init
再用命令方块超频执行function bounce:main
右键萝卜钓竿就可以发射弹弹苹果了!

一、运动分析
1. 首先我们来分析一下一个实体的运动过程

我们知道,速度是位置的变化率,加速度是速度的变化率

  1. v=dx/dt
  2. a=dv/dt
复制代码

此时的dt在mc里就是1tick
所以,位置的变化量x等于速度乘以1tick,速度的变化量等于加速度乘以1tick
此时速度的单位是block/tick,加速度单位为block/tick^2

2. 接下来分析一下实体与方块发生碰撞的情况
先来看平面中的情况
一个小球碰撞平面可以这样来看:
入射  出射
   \      /
    \    /
     \  /
      \/
------------- 碰撞平面
对入射时速度进行分解:
  -----> Vx
|
|
\|/  Vy
对出射时速度进行分解:
/|\ -Vy
|
|
  -----> Vx

可见发生反弹时垂直于反弹面的速度发生了反向。
将这个结论推广到三维空间中时也是成立的。

3. 再看两个实体碰撞的情况:
假设两个实体都是球体(简化模型)
微信截图_20180126193323.png
把速度分解到碰撞方向上
在碰撞方向上,满足动量定理。即
  1. m1*V1x+m2*V2x=m1*V1x'+m2*V2x'
复制代码

且两小球碰撞满足能量守恒,即
  1. 1/2 m1*V1^2+1/2 m2*V2^2=1/2 m1*V1'^2+1/2 m2*V2'^2
复制代码

联立即可解得V1x'、V2x'

二、下面就把上面的分析放到MC里来实现吧!

1. 准备工作
建立以下九个记分板
  1. scoreboard objectives add px dummy x坐标
  2. scoreboard objectives add py dummy y坐标
  3. scoreboard objectives add pz dummy z坐标
  4. scoreboard objectives add vx dummy x速度
  5. scoreboard objectives add vy dummy y速度
  6. scoreboard objectives add vz dummy z速度
  7. scoreboard objectives add ax dummy x加速度
  8. scoreboard objectives add ay dummy y加速度
  9. scoreboard objectives add az dummy z加速度
复制代码

用记分板记录实体的位置、速度和加速度

建立两个常数记分板和临时记分板来备用
  1. scoreboard objectives add temp dummy 临时1
  2. scoreboard objectives add temp2 dummy 临时2
  3. scoreboard objectives add const1 dummy 常数1
  4. scoreboard objectives add const2 dummy 常数2
复制代码


2. 对实体的初始化
首先生成一个实体
  1. summon minecraft:pig 8 10 8 {Tags:["new_ball"],NoGravity:1,NoAI:1,Health:1}
复制代码

再给它赋予一个速度
  1. scoreboard players set @e[tag=new_ball] vx 5000
  2. scoreboard players set @e[tag=new_ball] vy 5000
  3. scoreboard players set @e[tag=new_ball] vz 5000
复制代码

设置加速度。这里只有y方向有加速度,也就是重力加速度
最后如果想改变重力,只要修改这几个记分板就可以了!
  1. scoreboard players set @e[tag=new_ball] ax 0
  2. scoreboard players set @e[tag=new_ball] ay -200
  3. scoreboard players set @e[tag=new_ball] az 0
复制代码

设置一些常数,后面会用到
  1. scoreboard players set @e[tag=new_ball] const1 -1
复制代码

3. 开始运动!
建立一个函数名叫fly,其执行者就是要移动的实体
以下是fly.mcfunction的内容:
首先获取实体位置
  1. execute as @s store result score @s px run data get entity @s Pos[0] 100000
  2. execute as @s store result score @s py run data get entity @s Pos[1] 100000
  3. execute as @s store result score @s pz run data get entity @s Pos[2] 100000
复制代码

接着使速度变化量也就是加速度加到速度上
  1. scoreboard players operation @s vx += @s ax
  2. scoreboard players operation @s vy += @s ay
  3. scoreboard players operation @s vz += @s az
复制代码

接着,如果目前的位置加**置变化量也就是下一tick的位置有一个方块,那么我们就将速度反向,达到碰撞反弹的效果
把检测分为xyz三个方向来检测,这里用到了很多1.13的特性
首先生成一个实体来帮助检测
  1. summon minecraft:area_effect_cloud ~ ~ ~ {Tags:["temp_mark"]}
复制代码

接着移动实体的位置到下一tick的位置
  1. #判断有无方块-temp=px
  2. execute as @s store result score @s temp run scoreboard players get @s px
  3. #判断有无方块-temp加上vx
  4. scoreboard players operation @s temp += @s vx
  5. #判断有无方块-x移动临时实体
  6. execute as @s store result entity @e[tag=temp_mark,limit=1] Pos[0] double 0.00001 run scoreboard players get @s temp
复制代码

如果临时实体的位置处有方块,那么就反向vx
  1. execute at @e[tag=temp_mark,limit=1] unless block ~ ~ ~ air run scoreboard players operation @s vx *= @s const1
复制代码
此处const1的值为-1
再将临时实体的位置还原
  1. execute as @s store result entity @e[tag=temp_mark,limit=1] Pos[0] double 0.00001 run scoreboard players get @s px
复制代码

同理,对y,z方向都做同样处理。

本来我们是要检测碰撞另外一个实体的,但是由于速度在碰撞方向上的分解需要进行向量的旋转等运算,需要计算正弦正切值,太麻烦我懒得搞233反正思路有了肯定是可以做出来的!正弦的计算用泰勒展开就好!

接下来把位置加上速度,再把记分板的值更新到实体上,一个tick的循环就完成了!
  1. #移动
  2. #p加上v
  3. scoreboard players operation @s px += @s vx
  4. scoreboard players operation @s py += @s vy
  5. scoreboard players operation @s pz += @s vz
  6. #p存入nbt
  7. execute as @s store result entity @s Pos[0] double 0.00001 run scoreboard players get @s px
  8. execute as @s store result entity @s Pos[1] double 0.00001 run scoreboard players get @s py
  9. execute as @s store result entity @s Pos[2] double 0.00001 run scoreboard players get @s pz
  10. #删除临时实体
  11. kill @e[tag=temp_mark]
复制代码

三、继续优化

1. 能量损失
以上的碰撞都是完全弹性碰撞,如果想要变成有能量损失的碰撞,只需要这样:
首先新建两个记分板用来保存竖直方向和水平方向能量损失的系数
  1. scoreboard objectives add energy_lost_n dummy 碰撞法向能量损失
  2. scoreboard objectives add energy_lost_t dummy 碰撞切向能量损失
复制代码


实体初始化时新增修改一些常数:
  1. #设置常数
  2. scoreboard players set @e[tag=new_ball] energy_lost_n -95
  3. scoreboard players set @e[tag=new_ball] energy_lost_t 95
  4. scoreboard players set @e[tag=new_ball] const1 100
  5. scoreboard players set @e[tag=new_ball] const2 100000
复制代码


然后将碰撞后修改速度的命令
  1. execute at @e[tag=temp_mark,limit=1] unless block ~ ~ ~ air run scoreboard players operation @s vx *= @s const1
复制代码

改成执行一个函数
  1. execute at @e[tag=temp_mark,limit=1] unless block ~ ~ ~ air run function bounce:energy_lost/x
复制代码


energy_lost/x.mcfunction的内容如下:
  1. scoreboard players operation @s vx *= @s energy_lost_n
  2. scoreboard players operation @s vx /= @s const1
  3. scoreboard players operation @s vy *= @s energy_lost_t
  4. scoreboard players operation @s vy /= @s const1
  5. scoreboard players operation @s vz *= @s energy_lost_t
  6. scoreboard players operation @s vz /= @s const1
复制代码

修改三个方向的速度即可
注意到垂直于碰撞方向的常数是负数,意为速度反向

2. 停止运动
经过以上修改,实体一定会停在地面。
但是实际上,实体会在地面不断小幅度弹跳,一旦进入这个阶段,其实就是停在地面了。
这时候要判断出这种情况并让它不再弹跳

在energy_lost/y.mcfunction中加入以下代码:
表示如果竖直速度已经非常小了,就执行energy_lost/on_ground这个函数
  1. execute if score @s vy matches -999..999 run function bounce:energy_lost/on_ground
复制代码


energy_lost/on_ground.mcfunction的内容如下:
首先让实体的位置贴在地面上,再让竖直的速度和加速度为零。
  1. #修改py向下取整
  2. scoreboard players operation @s py /= @s const2
  3. scoreboard players operation @s py *= @s const2
  4. scoreboard players add @s py 1
  5. #设置速度为0
  6. scoreboard players set @s vy 0
  7. scoreboard players set @s ay 0
复制代码


经过以上的操作,基本的实体碰撞方块的物理引擎就写好啦~
我才不会说和pineapple撞车很尴尬呢233!

bounce.zip (5.92 KB, 下载次数: 19)

评分

参与人数 12人气 +28 金粒 +233 收起 理由
SPGoding + 4 + 50 MCBBS有你更精彩!
《企启》 + 2 + 30 julao
buhuichongfu + 2 + 10 剩下的人气都给你了,太棒了!.
switefaster + 1 + 10 在1.13里,你甚至可以把MC玩得物理.
雷鸣·翾鹗 + 2 神乎其技,不服不行!
luojiarui + 2 很好的作品
chyx + 3 复活!
Deing + 2 MC已经恐怖如斯
pineapple_ + 2 + 20 是不是该用动量守恒求撞车后的速度哈哈哈哈.
玄素 + 4 + 30 抱住
pca006132 + 2 + 50 物理物理
坑佑雷~ + 2 + 33 神乎其技,不服不行!

查看全部评分

leavessoft 当前离线
帖子
主题
精华
贡献
最后登录
1970-1-1
爱心
积分
6299
钻石
性别
保密
注册时间
2013-1-1
查看详细资料
发表于 2018-1-26 20:58:35 | 显示全部楼层
这个很厉害的样子,估计可以扩展成其他小游戏什么的……1.13更新似乎很厉害啊
回复

使用道具 举报

pineapple_ 当前离线
帖子
主题
精华
贡献
最后登录
1970-1-1
爱心
积分
5912
钻石
性别
保密
注册时间
2016-10-15
查看详细资料
发表于 2018-1-26 22:03:46 | 显示全部楼层
本帖最后由 pineapple_ 于 2018-1-26 22:09 编辑

←懒癌晚期玩家表示懒得做实体碰撞2333
不过你这个看起来,撞到草,水等可穿过方块也会弹回来诶
还有把分数负过来不需要*const1吧,精度直接取-0.00001就行了
"加速 位置"中的"速 位"是屏蔽词诶

回复

使用道具 举报

kongbaiyo 当前离线
帖子
主题
精华
贡献
最后登录
1970-1-1
爱心
积分
5207
钻石
性别
保密
注册时间
2014-3-14
查看详细资料
 楼主| 发表于 2018-1-27 17:44:42 | 显示全部楼层
pineapple_ 发表于 2018-1-26 22:03
←懒癌晚期玩家表示懒得做实体碰撞2333
不过你这个看起来,撞到草,水等可穿过方块也会弹回来诶
还有把分数 ...

确实会弹回来!不过只要在unless air那里把air换成可穿过的方块tag就好!

其实*const1是为了后面*energy_lost做伏笔的233 不过精度居然还能取负!我之前还不知道有这种操作!

迷之屏蔽词get233!
回复

使用道具 举报

我是MCCN 当前离线
帖子
主题
精华
贡献
最后登录
1970-1-1
爱心
积分
75
钻石
性别
保密
注册时间
2018-1-23
查看详细资料
发表于 2018-1-27 18:42:10 | 显示全部楼层
OMG!小白几乎完全看不懂(公式)
回复

使用道具 举报

chyx 当前离线
帖子
主题
精华
贡献
最后登录
1970-1-1
爱心
积分
17521
钻石
性别
保密
注册时间
2014-3-20
查看详细资料
发表于 2018-1-27 22:16:34 | 显示全部楼层
kongbaiyo 发表于 2018-1-27 17:44
确实会弹回来!不过只要在unless air那里把air换成可穿过的方块tag就好!

其实*const1是为了后面*energy ...

嗯 精度取负确实是基本操作XD

我在想 为啥实体间碰撞需要把速度分解到碰撞方向上呢?不分解为啥不行?
反正mc里实体也不能正常的旋转(船放在悬崖边缘 即使只有一点点和悬崖接触 也不会翻下去)
感觉直接把他们当质点 不分解速度不行吗?
回复

使用道具 举报

040128a 当前离线
帖子
主题
精华
贡献
最后登录
1970-1-1
爱心
积分
199
钻石
性别
保密
注册时间
2014-9-7
查看详细资料
发表于 2018-1-27 23:26:23 | 显示全部楼层
这个物理概念可以 挺好玩的 或许以后会被某个服务器用上?
回复

使用道具 举报

kongbaiyo 当前离线
帖子
主题
精华
贡献
最后登录
1970-1-1
爱心
积分
5207
钻石
性别
保密
注册时间
2014-3-14
查看详细资料
 楼主| 发表于 2018-1-29 10:44:57 | 显示全部楼层
chyx 发表于 2018-1-27 22:16
嗯 精度取负确实是基本操作XD

我在想 为啥实体间碰撞需要把速度分解到碰撞方向上呢?不分解为啥不行?

emmmm我这里假设实体是球体了

如果假设实体是正方体或者长方体的话就不用分解到速度方向上
如果假设是质点,那么两个物体理论上是永远不可能碰撞的

评分

参与人数 1人气 +3 收起 理由
chyx + 3 嗯 谢谢

查看全部评分

回复

使用道具 举报

MEMCA 当前离线
帖子
主题
精华
贡献
最后登录
1970-1-1
爱心
积分
597
钻石
性别
保密
注册时间
2014-10-7
查看详细资料
发表于 2018-1-29 14:57:47 来自手机 | 显示全部楼层
现在物理不好连游戏的玩不了了吗(?_?)
回复

使用道具 举报

雷鸣·翾鹗 当前离线
帖子
主题
精华
贡献
最后登录
1970-1-1
爱心
积分
3109
钻石
性别
保密
注册时间
2015-1-16
查看详细资料
发表于 2018-1-30 00:16:00 | 显示全部楼层
空白白为在mc里撞车做足了准备√【雾
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册(register)

本版积分规则

Archiver|小黑屋|Mcbbs.net ( 京ICP备15023768号-1 ) | 京公网安备 11010502037624号 | 手机版

GMT+8, 2019-11-19 02:41 , Processed in 0.050180 second(s), Total 18, Slave 17 queries , Gzip On, MemCached On.

"Minecraft"以及"我的世界"为Mojang Synergies AB的商标 本站与Mojang以及微软公司没有从属关系

© 2010-2019 我的世界中文论坛 版权所有 本站原创图文内容版权属于原创作者,未经许可不得转载

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