Unity DOTS《群体战斗弹幕游戏》核心技术分析之3D角色动画

最近DOTS发布了正式的版本, 我们来分享现在流行基于群体战斗的弹幕类游戏,实现的核心原理。今天给大家介绍大规模战斗群体3D角色的动画如何来实现。

DOTS 对角色动画支持的局限性

截止到Unity DOTS发布的版本1.0.16,目前还是无法很好的支持3D角色动画。在DOTS 的baker过程种,不支持常见的动画组件,包括: Animation组件,基于状态机的Animator, 以及人形动画。同时DOTS在节点Baker成Entity的过程种只支持MeshRenderer组件,不支持SkinnedMeshRenderer组件(SkinnedMeshRenderer组件很多转换过来后显示效果不正确)。所以DOTS要支持3D角色动画就变得非常的麻烦。目前主流的方案就两个:

(1)3D帧动画模式: 角色创作的时候使用MeshRender+材质不带动画,然后将动画组件每帧每个顶点的采样数据Bake到一张动画纹理,将这个纹理关联到材质里面。这样当物体Baker的时候,就可以正常地将MeshRender+材质转成Entity。在Shader中从动画纹理里读取动画每一帧,每个节点的模型坐标,再传递给渲染流程,把动画渲染出来。这样Entity就可以支持动画了。

(2) 将SkinnedMeshRender +动画做成ECS模式,然后Baker出来,这种Unity目前没有直接开放出来,有一些第三方的插件可以支持。

总之,如果要用DOTS基于ECS来播放3D角色动画,目前Unity DOTS版本(1.0.16)是不直接支持的。

《群体战斗弹幕游戏》应该使用哪种动画系统

《群体战斗弹幕游戏》这类游戏应该使用哪种动画系统会比较好呢?我们来分析一下项目需求,群体战斗有大量的战斗单元在一个屏幕,这些战斗单元对单个的渲染质量要求不高,但是数量巨大导致非常消耗性能。渲染质量要求不高,这样我们的战斗单元的模型的面数可以尽量的低,这样顶点数不会特别多,同时动画也不会很复杂,帧数也可以比较少。总结一下需求:单个模型顶点面数少,动画简单不复杂,节点的数目很多。

按照以上的需求,我们在群体战斗的游戏中选择《3D帧动画》模式来做大规模的角色动画。这个方式有几个好处,动画预先被计算生成到纹理里面,这样代替原来的动画采样计算,空间换时间节省了性能,同时面数少,动画简单,这样动画纹理的内存占用可以接受。针对《弹幕类游戏》有大量的同一种类型的物体,使用MeshRenderer+材质+动画纹理的模式,能最大限度地使用GPU Instancing合批,降低节约DrawCall。

基于帧动画的3D角色动画如何实现

基于帧动画的3D角色动画如何实现呢?结合弹幕游戏的3D角色动画简单的特点,分成以下步骤来具体实现:

  1. 基于URP来编写一个最简单的Diffuse Shader,渲染没有光照计算,阴影计算,直接将模型纹理贴到模型表面;

HLSLPROGRAM

#pragma vertex vert

#pragma fragment frag

// Includes

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DOTS.hlsl"

#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DOTS.hlsl"

struct appdata

{

float2 uv : TEXCOORD0;

float4 pos : POSITION;

UNITY_VERTEX_INPUT_INSTANCE_ID

};

struct v2f

{

float2 uv : TEXCOORD0;

float4 vertex : SV_POSITION;

UNITY_VERTEX_INPUT_INSTANCE_ID

};

CBUFFER_START(UnityPerMaterial)

sampler2D _MainTex;

float4 _MainTex_ST;

// float _AnimLen;

// sampler2D _AnimMap;

// float4 _AnimMap_TexelSize;//x == 1/width

CBUFFER_END

float4 ObjectToClipPos(float3 pos)

{

return mul(UNITY_MATRIX_VP, mul(UNITY_MATRIX_M, float4 (pos,1)));

}

v2f vert(appdata v, uint vid : SV_VertexID)

{

UNITY_SETUP_INSTANCE_ID(v);

v2f o;

o.uv = TRANSFORM_TEX(v.uv, _MainTex);

o.vertex = ObjectToClipPos(v.pos);

return o;

}

float4 frag(v2f i) : SV_Target

{

float4 col = tex2D(_MainTex, i.uv);

return col;

}

ENDHLSL

  1. 扩展Diffuse Shader,添加anim功能,增加uniform 来接受动画纹理,包括动画纹理对象(sampler2D),动画的时间长度(float);

v2f vert(appdata v, uint vid : SV_VertexID)

{

UNITY_SETUP_INSTANCE_ID(v);

float f = _Time.y / _AnimLen;

fmod(f, 1.0);

float animMap_x = (vid + 0.5) * _AnimMap_TexelSize.x;

float animMap_y = f;

float4 pos = tex2Dlod(_AnimMap, float4(animMap_x, animMap_y, 0, 0));

v2f o;

o.uv = TRANSFORM_TEX(v.uv, _MainTex);

o.vertex = ObjectToClipPos(pos);

return o;

}

  1. 基于简单的Diffuse Shader+传统的Animation组件做好普通的预制体节点给AnimBaker工具来使用

;

  1. 编写一个工具,将普通3D角色动画的预制体节点中的动画进行采样,把数据保存到动画纹理,并创建角色Mesh+材质(Difuse Anim Shader + Anim纹理+Anim时间)的预制体;
  1. 将生成好的预制体放到DOTS中的subscene来进行Bake,Bake成Entity,这样角色就可以带动画了。
相关推荐
stormsha7 小时前
裸眼3D原理浅析AI如何生成平面裸眼3D图像以科幻战士破框而出为例
人工智能·计算机视觉·平面·3d·ai
我一身正气怎能输16 小时前
游戏大厂A*寻路优化秘籍:流畅不卡顿
人工智能·游戏
AI生成未来18 小时前
从平面走向3D!中科大提出Pro3D-Editor,渐进式3D编辑新范式实现三维全视角精准一致
3d·3d生成
avi911121 小时前
发现一个宝藏Unity开源AVG框架,视觉小说的脚手架
unity·开源·框架·插件·tolua·avg
点金石游戏出海1 天前
玩家为何退出、不付费?读懂这些关键的“行为数据”,解锁增长密码!
游戏·数据分析·用户分析·游戏运营
看到我请叫我铁锤1 天前
vue3中THINGJS初始化步骤
前端·javascript·vue.js·3d
我一身正气怎能输1 天前
游戏导航网格地图如何自动切割成多边形区域——技术详解大白话长文
游戏
深海潜水员1 天前
【MonoGame游戏开发】| 牧场物语实现 第一卷 : 农场基础实现 (下)
vscode·游戏·c#·.net·monogame
有一个好名字2 天前
LeetCode跳跃游戏:思路与题解全解析
算法·leetcode·游戏
棒棒的皮皮2 天前
【Python】Open3d用于3D测高项目
python·3d·open3d