DOTS介绍+Unity DOTS-MAN小游戏项目实战

这篇具有很好参考价值的文章主要介绍了DOTS介绍+Unity DOTS-MAN小游戏项目实战。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言

DOTS是Unity在17年左右提出的一个概念,其核心是ECS。


提示:以下是本篇文章正文内容,下面案例可供参考

一、1. What is DOTS and why we use it?

全称:(Multi-Thread)Data-Oriented-Tech-Stack
(多线程式)数据导向型技术堆栈

1.DOTS包含的主要元素(三件套)

  1. 实体组件系统(ECS) - 提供使用面向数据的方法进行编码的框架。在Unity中它通过Entities软件包进行分发,您可以通过Package Manager来添加编辑器。
  2. C#作业系统 (JobSystem)- 提供一种生成多线程代码的简单方法。它通过Jobs软件包进行分发。
  3. Burst编译器 - 可生成快速、优化的本机代码。它通过Burst软件包进行分发,可通过Package Manager在编辑器中使用。
  4. 本机容器 - 属于ECS数据结构,可提供对内存的控制,值得注意的是Unity专门对内存管理进行了一部分优化以降低MissCache。

2.Why we use it?

  1. 许多并行编程范式,尤其是SIMD(单指令多数据)型范式,更倾向于使用SoA(结构体数组)。在CUDA C编程中也普遍倾向于SoA,一维数据元素是为全局内存的有效合并访问而预先准备好的,而相同内存操作引用的同字段元素在存储时时彼此相邻的,使用SoA能够显著减少MissCache。
  2. 实体组件系统(ECS)提供了一种面向数据的编码设计方法。利用面向数据的方法,可以对数据结构加以组织,以免出现高速缓存未命中的情况,从而令随后的数据访问更加高效、快捷。由于面向对象的设计并不专注于数据的组织,因此高速缓存未命中的情况很常见,这样就减慢了CPU访问数据的速度,因为它必须频繁地返回访问主内存中的数据。
  3. C#作业系统可以轻松地用C#编写快速、并行化的代码,以充分利用当今的多核处理器。
  4. Burst编译器会生成高度优化的代码,而这些代码可以利用您要编译的平台硬件。

Tips:

  1. jobsystem和ecs是两个不同的东西,但是配合起来使用会有1+1>2的效果
  2. burst与ecs的高度适配也使得ecs运行效率很高

3.Where we use it? (摘自Unity官方)

除非您在寻求短期或中期的性能改进,否则很难判定是否需要过渡到DOTS或何时过渡到DOTS。
DOTS几乎可以为每个应用程序带来一定程度的性能改进。这其中包括性能、电池使用寿命、迭代及项目可扩展性。过渡到DOTS不会造成任何性能的下降,但评估过渡到DOTS所增加的费用却至关重要,尤其是对于那些仅带来较小改进的项目。
对于所有应用程序而言,DOTS适合处理大量数据,例如开放式环境或使用大量相同材料的复杂结构。通过在实例之间共享公共数据以减少内存访问,DOTS也同样适用于重复的元素。
DOTS将来会帮助您开发高质量的内容,而不使用DOTS的Unity却很难做到,这一点务必要考虑清楚。例如,当今的标准游戏和Unity项目已经取代了过去的AAA游戏。放眼未来,您需要采用DOTS来保持竞争力。
针对不同的垂直行业,DOTS可以适用于不同的解决方案:

(1)对于AEC(工程建设)应用

  • DOTS适合处理大型数据集并确保内容的可扩展性。
  • DOTS非常适合进行大型交互式地图和具有大量模型和重复内容(例如建筑物和道路)的环境设计。
  • DOTS适用于复杂的工程可视化,可大规模地模拟现实环境。例如,DOTS非常适合进行粒度级工厂和基础架构设计。

(2) 对于汽车应用

  • 自动驾驶的仿真和可视化
  • DOTS非常适合进行大型交通和行人模拟,这需要成千上万的志愿Agent以逼真的方式移动和交互。

(3) 对于游戏独立开发者和自由职业者

  • DOTS可以帮助您减轻游戏中一些高成本操作的负担,并有助于提高性能,尤其是对于一些重复性进程。
  • 许多轻量级游戏(例如用于移动设备的游戏)并不能最大限度地提高硬件性能。即使有些游戏能够做到这一点,但这可能并不是它的主要关注点。不过,随着游戏的不断发展和硬件需求的持续增加,明智的做法是为将来使用DOTS做好准备。同样,Project Tiny也提供了使用DOTS开发较小应用程序和游戏的解决方案。
  • 如果您没有使用DOTS的迫切需求,那么最好先未雨绸缪,提高自己的DOTS技能,以便在DOTS成为Unity开发的标准方法时能够整装待发。

(4)对于游戏工作室

  • 当前格式的DOTS可以帮助您逐步达到Unity或其他方式所无法达到的规模和性能。具体而言,更长的电池使用寿命、温度控制以及DOTS所提供的代码可重用性是其主要优势所在。这些方面的性能改进还使您可以开发更多的低端设备,尤其是在西方市场以外的地区,这些设备会受到一定的硬件限制。
  • 通过让研发团队以DOTS开展工作,可以帮助您逐步了解所能采取的最佳方法,以及哪些最新的功能和领域最具性能优势和发展影响力。
  • DOTS并非要取代引擎团队的作用,而是让工程师腾出更多精力在自己的专业领域(例如阴影或着色器)进行创新。

4.DOTS的优劣(机遇以及风险)

在改善Unity项目的绩效方面,DOTS有着巨大的潜力。 但是,在使用DOTS时需要做出一些考量,它们会影响到项目的时间表、预算和开发团队。以下是一些需要与项目优先事项进行比较和对比的事项。这些事项可以归类为风险与机遇。

机遇

  1. 改进性能。默认情况下,我们经常使用“性能”一词来描述DOTS。这是什么意思呢?借助面向数据的设计和多线程,DOTS可以显著提升内存、运行时间和电池性能。随着游戏中显示的项目数量不断增加,提高性能的潜力也随之上升。相反,对于项目较少的游戏,您会发现游戏性能的改善程度却不太明显。
  2. 代码控制。随着项目规模的不断增大,DOTS可以更好地控制代码的复杂性。为DOTS编写的代码通常可以更好地分离关注点。因此,使用DOTS工作时,代码重构、编写单元测试以及在开发人员之间分配工作就变得更加容易。

风险

  1. 学习成本。如果您不熟悉DoD,那么面对DOTS时就会有一个学习曲线。尽管DoD在计算机科学领域有着良好的根基并已存在数年,而且DoD方法与OOP方法也有很大的不同,但DoD本质上并不比OOP复杂。ECS是一种不同于当前Unity MonoBehaviour方法的代码体系架构,因此学习需要一定的时间。目前,我们认为一名普通的Unity专业开发人员平均需要1个月才能熟练使用DOTS。这一准备时间可以被使用DOTS时的代码质量和性能改进所抵消。当然,具体要取决于项目。
  2. 有限支持。DOTS当前仅与Unity中一组有限的功能兼容。 最终,DOTS将与Unity的所有功能完全兼容,但我们目前尚无实现完全兼容的时间表。不过,DOTS允许在单个项目中同时使用游戏对象和DOTS,因此您可以将DOTS用于最频繁的处理任务,而将非DOTS Unity用于其余任务。
  3. 过渡。如果之前的项目是基于Mono开发,那么跟ECS之间的转换可能比较简单,使用Unity自带的一些Hybrid工具就可以较为简单的做到,但是想要把ECS转化为目前常用的Mono的话,我们认为可以做到,但是十分困难,而且也不建议这么做(为什么要尝试把高效率转为低效率呢)。目前比较推荐的是HybridECS开发,ECS与Mono混合在一起,ECS再配合Jobsystem处理最需要多线程的那一部分。

随着时间的推移,晶体管电路逐渐接近性能极限,在摩尔定律逐渐失效的今天,人们面临的数据也呈几何倍数暴增,我们有理由去发明并且学习使用一种效率更高,更能完全发挥硬件性能的软件编程方式,目前看来也许ECS也许能做到。

二、DOTS-Man小游戏项目实战

想要熟悉DOTS以及ECS框架,最好还是要上手做一个小项目,使用部分基础组件,想要熟悉以及精通还需要大量的练习以及使用,开发过程中要配合官方Entities文档使用。
Entities最新版本0.17的官方说明文档

1.环境配置

  • 如果是Unity2020.X以下版本:
    1. windows -> package manager
    2. advanced -> show preview package
    3. install三件套 (Entities,Jobs,Burst)
    4. install其他组件(Hybrid Renderer,Mathematics)

DOTS介绍+Unity DOTS-MAN小游戏项目实战

  • 如果是Unity2020.X及以上版本(推荐,作者使用2020.3.26f1c1):

    • 进入package manager
    • 点击 + 号点击add package from gir url手动添加三件套以及其他组件
      • com.unity.dots.editor
      • com.unity.physics
      • com.unity.entities
      • com.unity.rendering.hybrid

    DOTS介绍+Unity DOTS-MAN小游戏项目实战

2.游戏设计

我们准备做一个类似Pac-Man的小游戏,主要熟悉Physics包以及Entities的基本使用,所以不会开发怪物AI之类的,因为使用DOTS开发所以就叫DOTS-MAN好了。

需求分析

主要功能有:玩家移动,镜头跟随,分数显示,因为如果用ECS来修改UGUI的TEXT可能比较麻烦,这里选择使用HybridECS开发,使用MonoBehaviours开发一些基础功能比如镜头跟随以及物体生产之类。

3.正式开发

一些自带脚本

在开发过程中,因为收集物以及玩家还有地形之类的都要有碰撞,但是ECS无法使用object上面的collider之类的组件,所以就要用Entities包自带的一些脚本。

记得在挂Entities脚本之前删掉不用的Object脚本,避免混淆以及无意义的空间占用

把Object转化成Entity的脚本:
DOTS介绍+Unity DOTS-MAN小游戏项目实战
一般配合一起使用的脚本就是PhysicsShape和PhysicsBody,一个控制物理碰撞的类型,一个控制entity的物理性质(例如重力之类的),各个属性的作用都有明确说明:

DOTS介绍+Unity DOTS-MAN小游戏项目实战

添加physicsbody之后碰到List越界报错问题解决方案:
go into YOURPROJECTLibrary/PackageCache/
copy com.unity.collections@0.15.0-preview.21 into YOURPROJECT/Packages/
open com.unity.collections@0.15.0-preview.21\Unity.Collections\NativeList.cs
change line 599 from Allocator.None to Allocator.Invalid

Component

组件只有三个,两个存储分别存储移动和旋转的速度,一个负责标记收集物(所以里面没有数据)
要记得把Serializable属性改为GenerateAuthoringComponent,这样把component挂上object之后就会把他变成entity。
创建component和system都可以直接使用右键 -> create -> ECS进行快速选择自带模板
DOTS介绍+Unity DOTS-MAN小游戏项目实战

using Unity.Entities;

[GenerateAuthoringComponent]
public struct MoveComponent : IComponentData
{
    public float moveSpeed;
}
using Unity.Entities;

[GenerateAuthoringComponent]
public struct RotationComponent : IComponentData
{
    public float rotateSpeed; 
}

Component配置:
玩家:
DOTS介绍+Unity DOTS-MAN小游戏项目实战
墙体和收集物:

要注意在脚本中配置Collision Filter相关以及Collision Response相关,即某个entity属于哪个标签,他能与其他哪些标签的entity发生碰撞

DOTS介绍+Unity DOTS-MAN小游戏项目实战
搭建一个使用场景(renderer相关的根据自己喜好来整):

因为mono和ECS是相互穿插的,所以如果mono中有需要的system可以直接先去看看system的代码,配合官方文档理解为何这么做,这样才能把整个流程梳理清楚(至少我学习的时候是这样的)

Mono Behaviour

这里需要一个全局的mono behaviour来控制游戏,例如entity与object的连接,这里我们换一种方式,把之前的玩家小球弄成prefab,然后在这个全局mono控制玩家的生成,起名就叫做GameManager吧(具体说明看注释):

  1. GameManager:
using System.Collections;
using UnityEngine;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Physics;
using UnityEngine.UI;
using Unity.Transforms;

public class GameManager : MonoBehaviour
{
    public static GameManager instance;

    public bool insaneMode;

    //在实体object世界中的prefab
    public GameObject ballPrefab;
    public GameObject cubePrefab;
    public Text scoreText;

    public int maxScore;
    public int cubesPerFrame;
    public float cubeSpeed = 3f;

    private int curScore;
    private Entity ballEntityPrefab;
    private Entity cubeEntityPrefab;
    private EntityManager entityManager;
    private BlobAssetStore blobAssetStore;

    //private bool insaneMode;

    private void Awake()
    {
        if (instance != null && instance != this)
        {
            Destroy(gameObject);
            return;
        }

        instance = this;

        //初始化EntityManager
        entityManager = World.DefaultGameObjectInjectionWorld.EntityManager;
        blobAssetStore = new BlobAssetStore();

        //从object世界获得setting
        //即inspector中可以获取的prefab
        GameObjectConversionSettings settings = GameObjectConversionSettings.FromWorld(World.DefaultGameObjectInjectionWorld, blobAssetStore);

        //通过GameObjectConversionUtility的ConvertGameObjectHierarchy来把object变成entity
        //参数(GameObject root, World dstEntityWorld)
        ballEntityPrefab = GameObjectConversionUtility.ConvertGameObjectHierarchy(ballPrefab, settings);
        cubeEntityPrefab = GameObjectConversionUtility.ConvertGameObjectHierarchy(cubePrefab, settings);

    }

    private void OnDestroy()
    {
        //重置BlobAssetStore中的blobasset缓存,释放清空blobAssetStore
        blobAssetStore.Dispose();
    }

    private void Start()
    {
        curScore = 0;

        insaneMode = false;

        //显示分数,这个函数在每一帧都会调用
        DisplayScore();
        //创建初始球球
        SpawnBall();
    }

    private void Update()
    {
        //如果符合条件就开启insanemode疯狂造方块,这里改成手动开启
        //if (!insaneMode && curScore >= maxScore)
        if (insaneMode)
        {
            //开启协程造方块
            //insaneMode = true;
            StartCoroutine(SpawnLotsOfCubes());
        }
    }

    //回调,造方块
    IEnumerator SpawnLotsOfCubes()
    {
        while (insaneMode)
        {
            //每一帧造cubesPerFrame量的方块
            for (int i = 0; i < cubesPerFrame; i++)
            {
                SpawnNewCube();
            }
            yield return null;
        }
    }


    void SpawnNewCube()
    {
        //使用entityManager造方块并且给予属性

        Entity newCubeEntity = entityManager.Instantiate(cubeEntityPrefab);

        Vector3 direction = Vector3.up;
        Vector3 speed = direction * cubeSpeed;

        PhysicsVelocity velocity = new PhysicsVelocity()
        {
            Linear = speed,
            Angular = float3.zero
        };

        //最后记得往entity添加component数据
        entityManager.AddComponentData(newCubeEntity, velocity);
    }

    public void IncreaseScore()
    {
        curScore++;
        DisplayScore();
    }

    private void DisplayScore()
    {
        scoreText.text = "Score: " + curScore;
    }

    //造第一个球
    void SpawnBall()
    {
        Entity newBallEntity = entityManager.Instantiate(ballEntityPrefab);

        Translation ballTrans = new Translation
        {
            //初始位置
            Value = new float3(0f, 0.5f, 0f)
        };

        //还是要记得添加component
        entityManager.AddComponentData(newBallEntity, ballTrans);
        //设置镜头跟随的对象
        CameraFollow.instance.ballEntity = newBallEntity;
    }
}
  1. CameraFollow:
    相机跟随的mono脚本:
using Unity.Entities;
using Unity.Transforms;
using Unity.Mathematics;
using UnityEngine;

public class CameraFollow : MonoBehaviour
{
    public static CameraFollow instance;

    public Entity ballEntity;
    //设置一个偏移量用来调整相机位置
    public float3 offset;

    private EntityManager manager;

    private void Awake()
    {
        if (instance != null && instance != this)
        {
            Destroy(gameObject);
            return;
        }

        instance = this;
        manager = World.DefaultGameObjectInjectionWorld.EntityManager;
    }

    private void LateUpdate()
    {
        if (ballEntity == null) { return; }

        Translation ballPos = manager.GetComponentData<Translation>(ballEntity);
        transform.position = ballPos.Value + offset;
    }
}

记得把相机脚本挂到main camera上!

System

  1. MoveSystem:
    控制玩家移动,获取玩家输入放入一个float2中,具体的Mathematics相关class可以看官方文档,这是一个用起来比vector要快的东西(因为ECS是数据驱动,不用特别关注object):
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Physics;
using UnityEngine;

public class MoveSystem : SystemBase
{
    protected override void OnUpdate()
    {
        float deltaTime = Time.DeltaTime;

        float2 curInput = new float2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"));

        Entities.ForEach((ref PhysicsVelocity vel, ref MoveComponent speedData) =>
        {
            float2 newVel = vel.Linear.xz;

            newVel += curInput * speedData.moveSpeed * deltaTime;

            vel.Linear.xz = newVel;
        }).Run();
    }
}

相关要点:
ForEach就是对包含参数相关Component的entity在每一帧都进行一定的操作,其中ref关键字表示对数据进行读取也可以修改,而in关键字表示对数据只读,而且in一定要全部放在ref后面。
后面的的.Run()表示在主线程中运行,如果要在子线程可以使用Schedule。

  1. RotateSystem:
    控制收集物旋转的system,具体的quaternion用法可以参考官方文档:
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;

public class RotateSystem : SystemBase
{
    protected override void OnUpdate()
    {

        float deltaTime = Time.DeltaTime;

        Entities.ForEach((ref Rotation rotation, in RotationComponent rotationSpeed) =>
        {
            rotation.Value = math.mul(rotation.Value, quaternion.RotateX(math.radians(rotationSpeed.rotateSpeed * deltaTime)));
            rotation.Value = math.mul(rotation.Value, quaternion.RotateY(math.radians(rotationSpeed.rotateSpeed * deltaTime)));
            rotation.Value = math.mul(rotation.Value, quaternion.RotateZ(math.radians(rotationSpeed.rotateSpeed * deltaTime)));
        }).Run();
    }
}

记得这时候往你的object上面挂component!如果想让玩家移动就挂movecomponent,让收集物旋转就挂上rotationcomponent。可以想一想,如果你往收集物上挂了movecomponent会发生什么?为什么会这样?

这时候你的收集物应该是旋转的,玩家小球可以通过wasd或者方向键控制移动:
DOTS介绍+Unity DOTS-MAN小游戏项目实战
3. CollectSystem:
然后就是最难的碰撞收集系统了!本来在mono中两三行就可以解决的问题,现在要写几十行才能解决!但是对于后期优化以及性能上的提升,这些困难都不算什么!
相关的解释说明都在注释中了:

using Unity.Entities;
using Unity.Collections;
using Unity.Physics;
using Unity.Physics.Systems;

[UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
public class CollectSystem : SystemBase
{   
    //用bufferSystem来处理这些碰撞事件
    private EndFixedStepSimulationEntityCommandBufferSystem bufferSystem;
    //初始化entity的物理世界
    private BuildPhysicsWorld buildPhysicsWorld;
    private StepPhysicsWorld stepPhysicsWorld;

    protected override void OnCreate()
    {
        bufferSystem = World.GetOrCreateSystem<EndFixedStepSimulationEntityCommandBufferSystem>();
        buildPhysicsWorld = World.GetOrCreateSystem<BuildPhysicsWorld>();
        stepPhysicsWorld = World.GetOrCreateSystem<StepPhysicsWorld>();
    }

    protected override void OnUpdate()
    {
        //每一帧都添加一个triggerjob来进行碰撞判断,因为需要判断的是有MoveComponent的玩家
        //以及有DeleteTag的收集物,所以就要在job中进行选择
        Dependency = new TriggerJob
        {
            speedEntities = GetComponentDataFromEntity<MoveComponent>(),
            entitiesToDelete = GetComponentDataFromEntity<DeleteTag>(),
            commandBuffer = bufferSystem.CreateCommandBuffer(),
        }.Schedule(stepPhysicsWorld.Simulation, ref buildPhysicsWorld.PhysicsWorld, Dependency);

        //把job传递到buffer中
        bufferSystem.AddJobHandleForProducer(Dependency);

    }


    //创建一个triggerjob来进行碰撞处理
    private struct TriggerJob : ITriggerEventsJob
    {   
        //初始化处理的entity
        public ComponentDataFromEntity<MoveComponent> speedEntities;
        [ReadOnly] public ComponentDataFromEntity<DeleteTag> entitiesToDelete;
        public EntityCommandBuffer commandBuffer;

        public void Execute(TriggerEvent triggerEvent)
        {
            TestEntityTrigger(triggerEvent.EntityA, triggerEvent.EntityB);
            TestEntityTrigger(triggerEvent.EntityB, triggerEvent.EntityA);
        }

        //处理碰撞,如果被碰撞的物品没有DeleteTag,就把DeleteTag挂上去,移除它的物理组件
        private void TestEntityTrigger(Entity entity1, Entity entity2)
        {
            if (speedEntities.HasComponent(entity1))
            {
                if (entitiesToDelete.HasComponent(entity2)) { return; }
                commandBuffer.AddComponent<DeleteTag>(entity2);
                commandBuffer.RemoveComponent<PhysicsCollider>(entity2);
            }
        }
    }
}
  1. DeleteSystem:
    控制删除有deletetag的entity的system:
using Unity.Entities;

[UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
//这里添加一个属性,就是在collectionsystem发生之后再更新,因为要先碰撞之后再进行处理
[UpdateAfter(typeof(CollectSystem))]
public class DeleteSystem : SystemBase
{
    private EndFixedStepSimulationEntityCommandBufferSystem _endSimulationECBSystem;

    protected override void OnStartRunning()
    {
        _endSimulationECBSystem = World.GetOrCreateSystem<EndFixedStepSimulationEntityCommandBufferSystem>();
    }

    protected override void OnUpdate()
    {
        var ecb = _endSimulationECBSystem.CreateCommandBuffer();

        Entities
            .WithAll<DeleteTag>()
            .WithoutBurst()
            .ForEach((Entity entity) =>
            {    
                //修改分数
                GameManager.instance.IncreaseScore();
                ecb.DestroyEntity(entity);
            }).Run();
        _endSimulationECBSystem.AddJobHandleForProducer(Dependency);
    }
}

这里ForEach之前有一系列限定条件,比如.WithAll()的意思就是对带有deletetag的entity执行下面的操作,这样能更加方便的进行处理,所以大部分情况下entity都会被打一个标签来区别其他entity

最后需要进行的一些操作

  • 创建一个空物体放入GameManager,并且进行相关配置(可以在play模式下打开insaneMode看看ECS的强大性能提升):
    DOTS介绍+Unity DOTS-MAN小游戏项目实战
  • 在play模式下调整Camera中的相机跟随参数,让镜头舒服:

DOTS介绍+Unity DOTS-MAN小游戏项目实战

太棒啦!

太棒啦!你成功的使用了目前领先的开发模式开发了一个小游戏,虽然这个小游戏的功能在mono中实现的话可以很简单的实现,但是随着工程规模的扩大以及性能需求的提高,ECS只会愈发强大!因为目前DOTS相关教程不完善,所以如果在上述开发中碰到问题主要需要参考官方文档以及一些论坛大牛的解答,想要更深入的理解还需要更多项目的磨练。

3. 参考文档

Entities最新版本0.17的官方说明文档:
https://docs.unity3d.com/Packages/com.unity.entities@0.17/api/Unity.Entities.html
油管ECS大神Turbo的说明文档:
https://www.tmg.dev/tuts/roll-a-ball-entities-0-17/文章来源地址https://www.toymoban.com/news/detail-412450.html

到了这里,关于DOTS介绍+Unity DOTS-MAN小游戏项目实战的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Unity小游戏】游戏开发案例-Unity打造畅玩无阻的小游戏(上)

    乒乓克隆 使用立方体建造竞技场、球拍和球。 移动球和球拍。 击球并得分。 让相机感受到冲击力。 给游戏一个抽象的霓虹灯外观。 这是有关基础游戏的系列教程中的第一个教程。在其中,我们将创建一个简单的 Pong 克隆。 本教程是使用 Unity 2021.3.16f1 制作的。 本系列将涵

    2024年02月13日
    浏览(46)
  • 【Unity小游戏】《捕鱼达人》小游戏来啦~ 有源码下载【文末送书】

    📢博客主页:https://blog.csdn.net/zhangay1998 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! 📢本文由 呆呆敲代码的小Y 原创,首发于 CSDN 🙉 📢未来很长,值得我们全力奔赴更美好的生活✨

    2023年04月08日
    浏览(39)
  • Unity - 微信小游戏

    总参考:Unity WebGL 微信小游戏适配方案(公测)   下载 Unity插件,并导入至游戏项目中,版本更新请查看更新日志 请查阅推荐引擎版本,安装时选择 WebGL 组件 最终选择 Unity2021.2.5f1c1 InstantGame 前往Node官网安装长期稳定版 之前已安装 v16.17.0 前往微信开发者工具下载安装 Stable

    2024年02月06日
    浏览(58)
  • Unity小游戏——迷你拼图

    拼图演示 资源:  链接:https://pan.baidu.com/s/1BGeSmRCO_WZRUyl3MxefGw  提取码:0n4a 排列拼图碎片,拼出最后的图案。可以点住碎片的任意位置拖动;点击\\\"重来\\\"按钮,可以回到最初状态重新开始。 有很多电脑游戏的原型来自于现实世界中的玩具,拼图游戏就是其中的一个代表。 本

    2024年02月14日
    浏览(48)
  • Unity 开发微信小游戏

    今天给大家分享一下我近期用Unity开发微信小游戏的经过和踩坑; 微信小游戏就是微信小程序,小游戏是小程序的一个子集,小游戏是一种特殊的小程序;这篇文章建议大家收藏,干货满满 Unity开发微信小程序 Unity 3D下载官网 Unity转化小程序的Package包下载地址 微信开发者工

    2024年02月09日
    浏览(43)
  • Unity——2D小游戏笔记整理

    【每日一句:清晨和夜晚都请用尽全力去生活】 目录 一、环境搭建 二、人物 三、相机跟随人物移动 四、平铺精灵 五、血条跟随敌人行走 六、脚本逻辑 【玩家行走方法】 【玩家跳跃方法】 【改变玩家血量值方法】 【创建玩家子弹方法】 【主角血量,改变血条遮罩】 【敌

    2024年02月09日
    浏览(56)
  • unity小球吃金币小游戏

    链接放在这里 unity小球吃金币小游戏-Unity3D文档类资源-CSDN下载 这是我在学完虚拟现实技术这门课程后利用unity所做的小球吃金币小游戏,里面有源码和作品源文件,用u更多下载资源、学习资料请访问CSDN下载频道. https://download.csdn.net/download/m0_57324918/85604051 1创建Roll A Ball小球吃

    2023年04月08日
    浏览(48)
  • 【Unity】小球吃方砖小游戏

    目录 游戏背景 游戏开发         2.1场景布置         2.2小球运动         2.3镜头跟踪         2.4吃掉方砖         2.5结束提示 游戏录制           用wasd(↑←↓→)操控小球进行平面滑动,小球触碰会原地打转的立方体后立方体会消失,消除5个小球后提示

    2024年02月09日
    浏览(41)
  • unity转抖音小游戏笔记

    一.unity版本选择2019.4.8f1 二.需从BGDT内安装以下插件 StarkMini for Unity :基础小包改造 sdk,提供游戏改造小包的框架 Stark Asset Analyser :基于 starkmini 的自动化小包工具     https://microapp.bytedance.com/docs/zh-CN/mini-game/rd-to-SCgame/minify/overall 三.打开资源配置编辑器 ByteGame-StarkMini-Open

    2023年04月08日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包