Unity制作的高性能Minecraft

本文详细介绍了在Unity中实现Minecraft风格TNT爆炸效果的过程。通过自定义TNTBlockEntity类,利用协程控制爆炸延迟,并通过粒子系统和声音效果增强视觉冲击力。文章还展示了如何通过碰撞事件触发爆炸,以及如何处理爆炸范围内的方块破坏和实体受力。
项目地址: https://github.com/Jin-Yuhan/MinecraftClone-Unity 由于内容过多,这里只大概介绍下Editor的方块编辑器与TNT爆炸的实现。(未来可能会写一系列教程,完整实现这个项目)

方块编辑器

使用方块编辑器可以快速地新建方块,仅通过Flags与其他参数的组合可以制作出许多具有不同行为的方块。复杂方块的逻辑通常只需要将其注册在预设的事件中。

示例:TNT爆炸的实现

思路:在TNT被点击时,将TNT临时转换为方块实体,开启协程短暂延迟后爆炸。 首先,编写TNTBlockEntity,它继承于BlockEntity,其中BlockEntity编写了方块最基本的物理碰撞逻辑(基于AABB)。
    
using Minecraft . BlocksData ; using System . Collections ; using UnityEngine ; using System . Collections . Generic ; namespace Minecraft { [ DisallowMultipleComponent ] public sealed class TNTBlockEntity : BlockEntity { private MaterialPropertyBlock m_PropertyBlock ; private AudioSource m_AudioSource ; private bool m_Started ; private bool m_ShouldRenderBlock ; private AudioClip m_Fuse ; private AudioClip m_Explode ; private GameObject m_Effect ; protected override MaterialPropertyBlock MaterialPropertyBlock => m_PropertyBlock ; protected override bool ShouldRenderBlock => m_ShouldRenderBlock ; protected override void OnInitialize ( ) { m_PropertyBlock = new MaterialPropertyBlock ( ) ; m_AudioSource = gameObject . AddComponent < AudioSource > ( ) ; m_Started = false ; m_ShouldRenderBlock = true ; m_Fuse = Block . ExtraAssets [ 0 ] as AudioClip ; m_Explode = Block . ExtraAssets [ 1 ] as AudioClip ; m_Effect = Instantiate ( Block . ExtraAssets [ 2 ] ) as GameObject ; m_AudioSource . clip = m_Fuse ; m_AudioSource . Play ( ) ; } private void OnDestroy ( ) { Destroy ( m_Effect ) ; } protected override void OnCollisions ( CollisionFlags flags ) { if ( ! m_Started ) { m_Started = true ; StartCoroutine ( Explode ( ) ) ; } } private IEnumerator Explode ( ) { float time = 0 ; while ( time < 3 ) { time += Time . deltaTime ; float t = Mathf . Sin ( time * Mathf . PI * 3 ) ; Color color = Color . Lerp ( Color . grey , Color . white , t ) ; m_PropertyBlock . SetColor ( "_Color" , color ) ; yield return null ; } int radius = 5 ; WorldManager world = WorldManager . Active ; EntityManager entityManager = world . EntityManager ; Vector3Int pos = BlockPosition ; int x = pos . x ; int y = pos . y ; int z = pos . z ; for ( int dx = - radius ; dx <= radius ; dx ++ ) { for ( int dz = - radius ; dz <= radius ; dz ++ ) { for ( int dy = - radius ; dy <= radius ; dy ++ ) { if ( dx * dx + dy * dy + dz * dz <= radius * radius ) { Block block = world . GetBlock ( x + dx , y + dy , z + dz ) ; if ( block . HasAnyFlag ( BlockFlags . IgnoreExplosions ) ) continue ; world . SetBlockType ( x + dx , y + dy , z + dz , BlockType . Air ) ; } } } } IEnumerator < Entity > iterator = entityManager . EnumerateEntities ( ) ; while ( iterator . MoveNext ( ) ) { Entity entity = iterator . Current ; Vector3 dir = entity . transform . position - pos ; if ( dir . sqrMagnitude <= radius * radius ) { float vx = Random . Range ( - 10f , 10f ) ; //[-10, 10] float vy = Random . Range ( 1 , 10f ) ; float vz = Random . Range ( - 10f , 10f ) ; entity . Velocity = new Vector3 ( vx , vy , vz ) ; } } iterator . Dispose ( ) ; yield return null ; m_ShouldRenderBlock = false ; m_AudioSource . clip = m_Explode ; m_AudioSource . Play ( ) ; ParticleSystem particle = m_Effect . GetComponent < ParticleSystem > ( ) ; m_Effect . transform . position = transform . position ; particle . Play ( ) ; while ( m_AudioSource . isPlaying || particle ) { yield return null ; } entityManager . DestroyEntity ( this ) ; } } }
实现方块的OnClick事件并注册
    
public void BlockEvent_OnCick_TNT ( int x , int y , int z , Block block ) { WorldManager world = WorldManager . Active ; world . SetBlockType ( x , y , z , BlockType . Air ) ; TNTBlockEntity tnt = world . EntityManager . CreateEntity < TNTBlockEntity > ( ) ; tnt . Initialize ( x , y , z , block ) ; }
最终效果 其他内容请移步Github查看源码。本项目没有使用ECS,物理、光照、网格计算都是手敲的,除了Entity以外其他对象基本都不是MonoBehavior(包括Chunk),渲染是通过Graphics.DrawMesh方法实现。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值